TODO

passar tabelas pra csv(todas?), rename blank hashtag/media, juntar valores de reply pra cluster?

For R beginners

New chunk Ctrl+Alt+I

Execute chunk Ctrl+Shift+Enter

Execute all chunks Ctrl+Alt+R

HTML preview Ctrl+Shift+K

Library preparations

library(readr)
library(dplyr)
library(tidyverse)
library(ggplot2)
library(reshape2)
library(stats)

Data Import

data <- read.csv("~/4year/2semester/dtII/CSVs/HEIs.csv",
                 colClasses = c(tweet_id = "character"))

# Modifying created_at type so that attribute can be used more easily 
data$created_at <- as.POSIXct(data$created_at,
                              format= "%Y-%m-%dT%H:%M:%S", tz="UTC")

#View(data)
summary(data)
      id              tweet_id             text               type           bookmark_count    favorite_count     retweet_count      reply_count      
 Length:11728       Length:11728       Length:11728       Length:11728       Min.   :  0.000   Min.   :    0.00   Min.   :   0.00   Min.   :   0.000  
 Class :character   Class :character   Class :character   Class :character   1st Qu.:  0.000   1st Qu.:    7.00   1st Qu.:   2.00   1st Qu.:   0.000  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :  0.000   Median :   20.00   Median :   5.00   Median :   1.000  
                                                                             Mean   :  1.543   Mean   :   60.67   Mean   :  10.62   Mean   :   3.888  
                                                                             3rd Qu.:  1.000   3rd Qu.:   57.00   3rd Qu.:  11.00   3rd Qu.:   3.000  
                                                                             Max.   :418.000   Max.   :41655.00   Max.   :4214.00   Max.   :2317.000  
                                                                                                                                                      
   view_count        created_at                       hashtags             urls            media_type         media_urls       
 Min.   :      5   Min.   :2022-08-01 03:05:11.00   Length:11728       Length:11728       Length:11728       Length:11728      
 1st Qu.:   2643   1st Qu.:2022-10-19 12:56:27.00   Class :character   Class :character   Class :character   Class :character  
 Median :   6240   Median :2023-01-29 08:26:30.00   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
 Mean   :  14182   Mean   :2023-01-30 07:39:34.96                                                                              
 3rd Qu.:  16058   3rd Qu.:2023-05-05 14:16:43.25                                                                              
 Max.   :7604544   Max.   :2023-08-31 20:50:01.00                                                                              
 NA's   :4840                                                                                                                  

Initial Data Preparation

# Count of how many entries each HEI has
number_interactions <- data %>%
              group_by(id) %>% summarise(count = n())

number_interactions

Since complutense only has 1 entry we can’t learn anything from it, so we removed it

data <- data[data$id != "complutense.csv", ]

Visualization of number all posts, just tweets and just replies

number_posts <- data %>%
              group_by(id) %>% summarise(count = n())

number_tweets <- data[data$type == "Tweet", ] %>%
              group_by(id) %>% summarise(count = n())

number_replies <- data[data$type == "Reply", ] %>%
              group_by(id) %>% summarise(count = n())

print(number_posts)
print(number_tweets)
print(number_replies)

Calculating the percentage of tweets and replies based on all posts

# Merging the counts of tweets (count.y) and replies (count) with the count of posts (count.x)
data_ratio <- merge(number_posts, number_tweets, by = "id", all = TRUE)
data_ratio <- merge(data_ratio, number_replies, by = "id", all = TRUE)


data_ratio$percentage_tweets <- (data_ratio$count.y / data_ratio$count.x) * 100
data_ratio$percentage_replies <- (data_ratio$count / data_ratio$count.x) * 100

data_ratio <- data_ratio[, c("id", "percentage_tweets", "percentage_replies")]

print(data_ratio)

NA removal

Function to visualize the number of NAs in all columns

na_count <- function(){
  # Counting the number of NA values for each column
  na_count <- colSums(is.na(data))
  
  # Creating a new data frame with the NA counts
  na_counts_table <- data.frame(Column = names(na_count), NA_Count = na_count)
  
  print(na_counts_table)
}

Calculations of view, favourite, retweet and reply percentiles and visualization of NAs in all columns

data <- data %>%
  group_by(id) %>%
  mutate(view_percentile = ntile(view_count, 100),
         favorite_percentile = ntile(favorite_count, 100),
         retweet_percentile = ntile(retweet_count, 100),
         reply_percentile = ntile(reply_count, 100)) %>%
  rowwise() %>%
  mutate(avg_percentile = mean(c(view_percentile, favorite_percentile, retweet_percentile, reply_percentile), na.rm = TRUE))

na_count()

Calculation of the maximum number of views for each HEI

max_view_counts <- tapply(data$view_count, data$id, max, na.rm = TRUE)

Removal of NAs

# From view count
data$view_count <- ifelse(
  is.na(data$view_count),
  round(max_view_counts[data$id] * (data$avg_percentile / 100)),
  data$view_count)

# From view percentile
data$view_percentile <- ifelse(
  is.na(data$view_percentile),
  data$avg_percentile,
  data$view_percentile)

Visualization of NAs in all columns

na_count()

For now we’ll be only looking at tweets

data_tweets <- data[data$type == "Tweet", ]

data_tweets

Function to calculate average posts

average_tweets <- function(timeframe = "days"){
  # Calculation of the timeframe between earliest and latest post for each HEI
  date_range <- data_tweets %>%
    group_by(id) %>%
    summarise(min_date = min(created_at),
              max_date = max(created_at)) %>%
    mutate(num_days = as.numeric(difftime(max_date, min_date, units = timeframe)))
  
  # Naming the column respecting the timeframe
  column_name <- paste0("avg_tweets_per_", timeframe)
  
  # Calculation of the number of tweets per day for each HEI
  tweets_per_timeframe <- number_tweets %>%
    left_join(date_range, by = "id") %>%
    mutate(!!column_name := count / num_days)
  
  print(tweets_per_timeframe)
  return(tweets_per_timeframe)
}
tweets_per_day <- average_tweets()
tweets_per_week <- average_tweets(timeframe = "weeks")

Plot for the average number of tweets per day for each HEI

barplot(tweets_per_day$avg_tweets_per_days,
        names.arg = tweets_per_day$id,
        main = "Average Tweets per Day",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(tweets_per_day$avg_tweets_per_days) + 1),
        las = 2,
        col = "#3498DB")

# Adding text labels over each bar and aligning it with the center of each bar 
text(x = barplot(tweets_per_day$avg_tweets_per_days, plot = FALSE),
     y = tweets_per_day$avg_tweets_per_days,
     labels = round(tweets_per_day$avg_tweets_per_days, 2),
     pos = 3)

Plot for the average number of tweets per week for each HEI

barplot(tweets_per_week$avg_tweets_per_weeks,
        names.arg = tweets_per_week$id,
        main = "Average Tweets per Week",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(tweets_per_week$avg_tweets_per_weeks) + 5),
        las = 2,
        col = "#E74C3C")

text(x = barplot(tweets_per_week$avg_tweets_per_weeks, plot = FALSE),
     y = tweets_per_week$avg_tweets_per_weeks,
     labels = round(tweets_per_week$avg_tweets_per_weeks, 2),
     pos = 3)

Defining the intervals of time for the academic year

intervals <- list(
  interval1 = as.POSIXct(c("2022-08-31", "2022-12-15")),
  interval2 = as.POSIXct(c("2023-01-04", "2023-04-01")),
  interval3 = as.POSIXct(c("2023-04-14", "2023-06-15"))
)

Function to check if a date falls within a given interval of time and apply appropriate Boolean

check_interval <- function(date) {
  for (i in 1:length(intervals)) {
    interval_start <- intervals[[i]][1]
    interval_end <- intervals[[i]][2]
    if (date >= interval_start & date <= interval_end) {
      return(TRUE)
    }
  }
  return(FALSE)
}
data_tweets$academic_year <- sapply(data_tweets$created_at, check_interval)
print(data.frame(id = data_tweets$id, academic_year = data_tweets$academic_year))

Plot for the number of tweets per timeframe of either vacation or academic time

barplot(table(data_tweets$academic_year),
        main = "Number of Tweets per Timeframe",
        xlab = "Time",
        ylab = "Count",
        ylim = c(0, max(table(data_tweets$academic_year)) + 1000),
        names.arg = c("Vacation", "Academic"),
        col = c("#8E44AD", "#F1C40F"))

text(x = barplot(data_tweets$academic_year, plot = FALSE), 
     y = table(data_tweets$academic_year) + 0.5, 
     labels = table(data_tweets$academic_year), 
     pos = 3)

Function to count number of tweets and average per day

analyze_tweets <- function(academic_year_filter = TRUE) {
  # Filtering the data based on the academic_year_filter
  filtered_data <- data_tweets %>%
    filter(academic_year == academic_year_filter)
  
  # Count of days for each HEI
  unique_days <- filtered_data %>%
    group_by(id) %>%
    summarise(unique_days = n_distinct(as.Date(created_at)))
  
  # Count of tweets for each HEI
  number_tweets_boolean <- filtered_data %>%
    group_by(id) %>%
    summarise(count = n())
  
  # Naming the column respecting the time period
  year <- ifelse(academic_year_filter, "academic_time", "vacation_time")
  column_name <- paste0("avg_tweets_in_", year)
  
  # Combination of data and calculation of average posts per day
  combined_data <- left_join(unique_days, number_tweets_boolean, by = "id")
  combined_data <- combined_data %>%
    mutate(!!column_name := count / unique_days)
  
  print(combined_data)
  return(combined_data)
}
data_tweets_academic <- analyze_tweets()
data_tweets_vacations <- analyze_tweets(academic_year_filter = FALSE)

Plot for the average number of tweets during academic time for each HEI

barplot(data_tweets_academic$avg_tweets_in_academic_time,
        names.arg = data_tweets_academic$id,
        main = "Average Tweets during Academic Time",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(data_tweets_academic$avg_tweets_in_academic_time) + 5),
        las = 2,
        col = "#34495E")

text(x = barplot(data_tweets_academic$avg_tweets_in_academic_time, plot = FALSE),
     y = data_tweets_academic$avg_tweets_in_academic_time,
     labels = round(data_tweets_academic$avg_tweets_in_academic_time, 2),
     pos = 3)

Plot for the average number of tweets during vacation time for each HEI

barplot(data_tweets_vacations$avg_tweets_in_vacation_time,
        names.arg = data_tweets_vacations$id,
        main = "Average Tweets during Vacation Time",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(data_tweets_vacations$avg_tweets_in_vacation_time) + 5),
        las = 2,
        col = "#D35400")

text(x = barplot(data_tweets_vacations$avg_tweets_in_vacation_time, plot = FALSE),
     y = data_tweets_vacations$avg_tweets_in_vacation_time,
     labels = round(data_tweets_vacations$avg_tweets_in_vacation_time, 2),
     pos = 3)

Data preparation for day of the week

# Creating new table that contains a new column for the day of the week
data_tweets_days <- data_tweets %>%
  mutate(day_of_week = weekdays(created_at))

# Selecting only the id, created_at, and day_of_week columns for the new table
data_tweets_days <- data_tweets_days %>%
  select(id, created_at, day_of_week)

print(data_tweets_days)
# Grouping by id and day_of_week, then counting the number of tweets
number_tweets_days <- data_tweets_days %>%
  group_by(id, day_of_week) %>%
  summarise(count = n())
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
# Grouping by id, day_of_week and day created at, then counting the number of tweets
number_tweets_per_day <- data_tweets_days %>%
  mutate(created_date = as.Date(created_at)) %>%
  group_by(id, day_of_week, created_date) %>%
  summarise(count = n())
`summarise()` has grouped output by 'id', 'day_of_week'. You can override using the `.groups` argument.
# Finding for each HEI the average count of tweets per day
average_number_tweets_per_day <- number_tweets_per_day %>%
  group_by(id, day_of_week) %>%
  summarise(average_count = mean(count))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
print(number_tweets_days)

Highest and lowest tweets

# Finding the HEI with the lowest count of tweets per day
lowest_count <- number_tweets_days %>%
  group_by(day_of_week) %>%
  slice_min(order_by = count) %>%
  select(day_of_week, id, count)

# Finding the HEI with the highest count of tweets per day
highest_count <- number_tweets_days %>%
  group_by(day_of_week) %>%
  slice_max(order_by = count) %>%
  select(day_of_week, id, count)

# Combine the results
high_low_HEI <- bind_rows(lowest_count, highest_count) %>%
  arrange(day_of_week)

print(high_low_HEI)

Plot for the lowest and highest count of tweets per day for each day of the week

ggplot(high_low_HEI, aes(x = day_of_week, y = count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = count),
            position = position_dodge(width = 0.9),
            vjust = -0.5,
            size = 3) +
  labs(title = "Lowest and Highest Count of Tweets per Day for Each Day of the Week",
       x = "Day of the Week", y = "Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Average of tweets

# Finding the HEI with lowest and highest averaged count of tweets per day
high_low_average_HEIs <- average_number_tweets_per_day %>%
  group_by(day_of_week) %>%
  filter(average_count == max(average_count) | average_count == min(average_count)) %>%
  arrange(day_of_week, ifelse(average_count == min(average_count), average_count, -average_count))

print(high_low_average_HEIs)

Plot for the highest and lowest average count of tweets per day for each day of the week

ggplot(high_low_average_HEIs, aes(x = day_of_week, y = average_count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = round(average_count, 2)),
            position = position_dodge(width = 0.7),
            vjust = -0.5,
            size = 3) +
  labs(title = "Highest and Lowest Average Count of Tweets per Day for Each Day of the Week",
       x = "Day of the Week", y = "Average Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Views Likes Retweets and Replies

# Table containing views, likes, retweets and replies for each media type for each HEI
types_of_tweets <- data_tweets %>%
              group_by(id, media_type) %>%
              summarise(count = n(),
                        views = sum(view_count, na.rm = TRUE),
                        likes = sum(favorite_count, na.rm = TRUE),
                        retweets = sum(retweet_count, na.rm = TRUE),
                        replies = sum(reply_count, na.rm = TRUE))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
                        
print(types_of_tweets)                        
# Grouping by HEI and calculating the total values of views, likes and replies across all media types
total_tweets_stats <- types_of_tweets %>%
  group_by(id) %>%
  summarise(total_views = sum(views),
            total_likes = sum(likes),
            total_replies = sum(replies))

print(total_tweets_stats)

Function for piechart creation for views, likes and replies

pie_maker <- function(target_id = "duke.csv"){
  # Filtering data for the specific HEI
  hei_data <- types_of_tweets %>%
    filter(id == target_id)
  
  # Calculating total views for each media type for the specific HEI
  hei_media <- hei_data %>%
    group_by(media_type) %>%
    summarise(total_views = sum(views),
              total_likes = sum(likes),
              total_replies = sum(replies))
  
  # Calculating the percentage of views for each media type for the specific HEI
  hei_media$percentage_view <- hei_media$total_views / sum(hei_media$total_views) * 100
  hei_media$percentage_like <- hei_media$total_likes / sum(hei_media$total_likes) * 100
  hei_media$percentage_reply <- hei_media$total_replies / sum(hei_media$total_replies) * 100
  
  # Creating the pie chart for views
  hei_pie_chart_views <- ggplot(hei_media, aes(x = "", y = percentage_view, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_views, "(", round(percentage_view, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#2196F3", "animated_gif" = "#E67E22", "photo" = "#8E44AD", "video" = "#138D75")) +
    labs(title = paste("Views for each media type -", target_id))
  
  # Creating the pie chart for likes
  hei_pie_chart_likes <- ggplot(hei_media, aes(x = "", y = percentage_like, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_likes, "(", round(percentage_like, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#E91E63", "animated_gif" = "#4A148C", "photo" = "#90CAF9", "video" = "#00BFA5")) +
    labs(title = paste("Likes for each media type -", target_id))
  
  # Creating the pie chart for replies
  hei_pie_chart_replies <- ggplot(hei_media, aes(x = "", y = percentage_reply, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_replies, "(", round(percentage_reply, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#666600", "animated_gif" = "#99CCCC", "photo" = "#9966CC", "video" = "#330000")) +
    labs(title = paste("Replies for each media type -", target_id))
  
  # Print the pie charts
  print(hei_pie_chart_views)
  print(hei_pie_chart_likes)
  print(hei_pie_chart_replies)
}

Plot of piecharts for each HEI

pie_maker()

pie_maker("epfl.csv")

pie_maker("goe.csv")

pie_maker("harvard.csv")

pie_maker("leicester.csv")

pie_maker("manchester.csv")

pie_maker("mit.csv")

pie_maker("sb.csv")

pie_maker("stanford.csv")

pie_maker("trinity.csv")

pie_maker("wv.csv")

pie_maker("yale.csv")

# Calculation of like_ratio and replies_ratio percentages
ratios_tweets_table <- total_tweets_stats %>%
  mutate(like_ratio = total_likes / total_views * 100,
         replies_ratio = total_replies / total_views * 100)

# Creation of new table with each HEI, like_ratio, and replies_ratio 
hei_tweets_ratios <- ratios_tweets_table %>%
  select(id, like_ratio, replies_ratio) %>%
  distinct()

print(hei_tweets_ratios)

Plot for like_ratio and replies_ratio for each HEI

ggplot(hei_tweets_ratios, aes(x = id)) +
  geom_bar(aes(y = like_ratio, fill = "Like Ratio"), stat = "identity", position = "dodge") +
  geom_bar(aes(y = replies_ratio, fill = "Replys Ratio"), stat = "identity", position = "dodge") +
  geom_text(aes(y = like_ratio, label = round(like_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#000000") +
  geom_text(aes(y = replies_ratio, label = round(replies_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#FFFFFF") +
  labs(title = "Like and Replys Ratios by HEI",
       x = "HEI",
       y = "Ratio (%)",
       fill = "Metric") +
  scale_fill_manual(values = c("Like Ratio" = "#2196F3", "Replys Ratio" = "#F44336")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

# Table with averages of views, likes, retweets and replies
types_of_tweets_per_tweet <- types_of_tweets %>%
                        group_by(id, media_type) %>%
                        summarise(avg_views = mean(views / count),
                                  avg_likes = mean(likes / count),
                                  avg_retweets = mean(retweets / count),
                                  avg_replies = mean(replies / count))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
print(types_of_tweets_per_tweet)
# Grouping by HEI and calculating the average values of views, likes and replies across all media types
total_average_stats <- types_of_tweets_per_tweet %>%
  group_by(id) %>%
  summarise(avg_views = sum(avg_views),
            avg_likes = sum(avg_likes),
            avg_replies = sum(avg_replies))

print(total_average_stats)
# Calculation of like_ratio and replies_ratio percentages
ratios_average_table <- total_average_stats %>%
  mutate(like_ratio = avg_likes / avg_views * 100,
         replies_ratio = avg_replies / avg_views * 100)

# Creation of new table with each HEI, like_ratio, and replies_ratio 
hei_average_ratios <- ratios_average_table %>%
  select(id, like_ratio, replies_ratio) %>%
  distinct()

print(hei_average_ratios)

Plot for like_ratio and replies_ratio for each HEI

ggplot(hei_average_ratios, aes(x = id)) +
  geom_bar(aes(y = like_ratio, fill = "Like Ratio"), stat = "identity", position = "dodge") +
  geom_bar(aes(y = replies_ratio, fill = "Replies Ratio"), stat = "identity", position = "dodge") +
  geom_text(aes(y = like_ratio, label = round(like_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#000000") +
  geom_text(aes(y = replies_ratio, label = round(replies_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#FFFFFF") +
  labs(title = "Like and Replies Ratios by HEI",
       x = "HEI",
       y = "Ratio (%)",
       fill = "Metric") +
  scale_fill_manual(values = c("Like Ratio" = "#330066", "Replies Ratio" = "#FF6666")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Hashtags

# Table with number of unique hashtags
unique_hashtags <- data_tweets %>%
                group_by(id) %>%
                summarise(count = n(),
                          unique_hashtags = length(unique(hashtags)))

print(unique_hashtags)

Plot for the count of unique hashtags for each HEI

barplot(unique_hashtags$unique_hashtags,
        names.arg = unique_hashtags$id,
        main = "Unique Hashtags for Each HEI",
        xlab = "HEI",
        ylab = "Count of Unique Hashtags",
        ylim = c(0, max(unique_hashtags$unique_hashtags) + 50),
        las = 2,
        col= "#16A085")

text(x = barplot(unique_hashtags$unique_hashtags, plot = FALSE),
     y = unique_hashtags$unique_hashtags,
     labels = round(unique_hashtags$unique_hashtags, 2),
     pos = 3)

Heatmaps

# Create column hour from created_at
data_tweets_days$created_hour <- as.numeric(format(data_tweets_days$created_at, "%H"))

Function to plot heatmap for various HEIs

heatmap_maker <- function(target_id = "duke.csv"){
  # Filtering data for the specific HEI
  target_data <- data_tweets_days %>%
    filter(id == target_id)
  
  # Grouping by day of the week and hour, and counting the number of tweets
  tweet_counts <- target_data %>%
    group_by(day_of_week, created_hour) %>%
    summarise(num_tweets = n())
  
  # Plotting heatmap
  ggplot(tweet_counts, aes(x = day_of_week, y = created_hour, fill = num_tweets)) +
    geom_tile() +
    scale_fill_gradient(low = "white", high = "blue") +
    labs(title = paste("Tweet Heatmap for", target_id),
         x = "Day of the week",
         y = "Hour of the day")
}

heatmap_maker()
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("epfl.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("goe.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("harvard.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("leicester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("manchester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("mit.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("sb.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("stanford.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("trinity.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("wv.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("yale.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

Text

data_tweets_content <- data_tweets %>%
            select(id, text)

# Counting number of words
data_tweets_content <- data_tweets_content %>%
  mutate(num_words = lengths(strsplit(text, "\\s+")))

print(data_tweets_content)

# Grouping by HEI and calculate average, minimum, and maximum values of number of words
data_tweets_content_metrics <- data_tweets_content %>%
  group_by(id) %>%
  summarise(average_num_words = mean(num_words),
            min_num_words = min(num_words),
            max_num_words = max(num_words))
print(data_tweets_content_metrics)

Plot for the average, maximum and minimum values of words for each HEI

ggplot(data_tweets_content_metrics, aes(x = id, y = average_num_words)) +
  geom_point(aes(color = "Average")) +
  geom_errorbar(aes(ymin = min_num_words, ymax = max_num_words, color = "Range"), width = 0.2) +
  scale_color_manual(values = c("Average" = "#1976D2", "Range" = "#EF5350")) +
  labs(title = "Word Count Summary by HEI",
       x = "HEI",
       y = "Number of Words",
       color = "Metric") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Now replies

data_replies <- data[data$type == "Reply", ]

data_replies

Interactions to replies

# Table containing views, likes, retweets and replies for each media type for each HEI
types_of_replies <- data_replies %>%
              group_by(id, media_type) %>%
              summarise(count = n(),
                        views = sum(view_count, na.rm = TRUE),
                        likes = sum(favorite_count, na.rm = TRUE),
                        retweets = sum(retweet_count, na.rm = TRUE),
                        replies = sum(reply_count, na.rm = TRUE))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
                        
print(types_of_replies)                        
# Grouping by HEI and calculating the total values of views, likes and replies across all media types
total_replies_stats <- types_of_replies %>%
  group_by(id) %>%
  summarise(total_views = sum(views),
            total_likes = sum(likes),
            total_replies = sum(replies))

print(total_replies_stats)
# Calculation of like_ratio and replies_ratio percentages
ratios_replies_table <- total_replies_stats %>%
  mutate(like_ratio = total_likes / total_views * 100,
         replies_ratio = total_replies / total_views * 100)

# Creation of new table with each HEI, like_ratio, and replies_ratio 
hei_replies_ratios <- ratios_replies_table %>%
  select(id, like_ratio, replies_ratio) %>%
  distinct()

print(hei_replies_ratios)

Clusters

# Creating table for cluster algorithms

# Joining attribute count (number of tweets) and unique_hashtags (number of unique hashtags) per HEI
cluster_table <- merge(select(unique_hashtags, id, unique_hashtags), select(number_tweets, id, count), by = "id", all=TRUE)

# Joining attribute avg_tweets_per_days (average of tweets per day) per HEI
cluster_table <- merge(cluster_table, select(tweets_per_day, id, avg_tweets_per_days), by = "id", all=TRUE)

# Joining attribute avg_tweets_per_weeks (average of tweets per week) per HEI
cluster_table <- merge(cluster_table, select(tweets_per_week, id, avg_tweets_per_weeks), by = "id", all=TRUE)

# Joining attribute avg_tweets_in_academic_time (average of tweets during academic time) per HEI
cluster_table <- merge(cluster_table, select(data_tweets_academic, id, avg_tweets_in_academic_time), by = "id", all=TRUE)

# Joining attribute avg_tweets_in_vacation_time (average of tweets during vacation time) per HEI
cluster_table <- merge(cluster_table, select(data_tweets_vacations, id, avg_tweets_in_vacation_time), by = "id", all=TRUE)

# Joining attribute total_views (total number of views), total_likes (total number of likes) and total_replies (total number of replies) per HEI
cluster_table <- merge(cluster_table, select(total_tweets_stats, id, total_views, total_likes, total_replies), by = "id", all=TRUE)

# Renaming attribute like_ratio to total_like_ratio and replies_ratio to total_replies_ratio
cluster_table <- merge(cluster_table, select(hei_tweets_ratios, id, like_ratio, replies_ratio), by = "id", all=TRUE)
cluster_table <- cluster_table %>%
  rename(total_like_ratio = like_ratio, 
         total_replies_ratio = replies_ratio)

# Joining attribute avg_views (average number of views), avg_likes (average number of likes) and avg_replies (average number of replies) per HEI
cluster_table <- merge(cluster_table, select(total_average_stats, id, avg_views, avg_likes, avg_replies), by = "id", all=TRUE)

# Renaming attribute like_ratio to avg_like_ratio and replies_ratio to avg_replies_ratio
cluster_table <- merge(cluster_table, select(hei_average_ratios, id, like_ratio, replies_ratio), by = "id", all=TRUE)
cluster_table <- cluster_table %>%
  rename(avg_like_ratio = like_ratio, 
         avg_replies_ratio = replies_ratio)

print(cluster_table)

Function for cluster method

cluster_maker <- function(seed = 123, num_clusters = 3, table){
  set.seed(123)
  
  # Excluding id column for clustering
  cluster_data <- select(table, -id)
  
  # Scaling the data for kmeans method
  scaled_data <- scale(cluster_data)
  
  kmeans_result <- kmeans(scaled_data, centers = num_clusters)
  
  print(kmeans_result$centers)
  print(kmeans_result$cluster)
  
  return(kmeans_result)
}

Function to add ids to better visualize results

cluster_id_maker <- function(kmeans_result, table){
  # Merging the cluster assignments with the original data
  cluster_assignments <- data.frame(id = table$id, cluster = kmeans_result$cluster)

  print(cluster_assignments)
  plot(kmeans_result$cluster)
}

Three clusters with seed 123

cluster_123_3 <- cluster_maker(table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
2      -0.4672956  1.2357198           1.2354314            1.2354314                   1.1911790                   1.2741657   2.0971401  1.93751241
3       0.1740030 -0.1713844          -0.1731538           -0.1731538                  -0.1731809                  -0.1913303  -0.4153661 -0.43904812
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1     0.5534067        2.0996788           3.0684868 -0.3467066  1.9304181   2.3142417      2.6030420         3.1020546
2     1.6810073       -0.7177677          -0.4777518  1.7460208  1.4388566   1.1599670     -0.6396304        -0.4169707
3    -0.4350468       -0.0737937          -0.2347759 -0.3494817 -0.5342368  -0.5149084     -0.1470868        -0.2520126
 [1] 3 3 3 2 3 3 2 3 1 3 3 3
cluster_id_maker(cluster_123_3, table = cluster_table)

Seven clusters with seed 123

cluster_123_6 <- cluster_maker(num_clusters = 7, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1     -0.54099099 -0.7980288          -0.7998764           -0.7998764                  -0.8566746                  -0.7686479  -0.4659280 -0.34870651
2     -0.33665384  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   1.6390693  2.76526795
3      2.06849496  0.3163906           0.3164848            0.3164848                   0.3487604                   0.3235393  -0.4385686 -0.45795609
4     -0.36345215 -0.5258824          -0.5295450           -0.5295450                  -0.5074127                  -0.5879623  -0.3946334 -0.54854585
5     -0.59793741  0.3734033           0.3706973            0.3706973                   0.1671229                   0.3341520   2.5552108  1.10975687
6     -0.63143530 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
7     -0.03517279  1.5243464           1.5265794            1.5265794                   1.4868514                   1.5200937  -0.3507682 -0.14392452
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1   -0.44336928        1.2911568          0.07105937 -0.3861273 -0.3579270  -0.4728730      0.8212504       -0.10327394
2    2.91587745       -0.6417247         -0.40143781  0.3936984  1.4654190   1.5919118     -0.5339425       -0.30190583
3   -0.46641612       -0.2251792         -0.13401717 -0.3929316 -0.6804893  -0.5991306     -0.2493630       -0.12958214
4   -0.51767840       -0.6173697         -0.47022920 -0.3036546 -0.5504837  -0.5144803     -0.5587944       -0.43018681
5    0.44613716       -0.7938108         -0.55406573  3.0983433  1.4122942   0.7280222     -0.7453183       -0.53203567
6    0.55340665        2.0996788          3.06848680 -0.3467066  1.9304181   2.3142417      2.6030420        3.10205460
7   -0.02513687       -0.3266195         -0.10615086 -0.3725990 -0.5293640  -0.4322473     -0.2323782       -0.08165369
 [1] 4 4 1 2 3 4 5 4 6 3 1 7
cluster_id_maker(cluster_123_6, table = cluster_table)

Five clusters with seed 123

cluster_123_6 <- cluster_maker(num_clusters = 5, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
2      -0.3366538  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   1.6390693  2.76526795
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.4093018 -0.35327890
4      -0.4226318 -0.6165979          -0.6196555           -0.6196555                  -0.6238333                  -0.6481908  -0.4183983 -0.48193274
5      -0.5979374  0.3734033           0.3706973            0.3706973                   0.1671229                   0.3341520   2.5552108  1.10975687
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1     0.5534067       2.09967875           3.0684868 -0.3467066  1.9304181   2.3142417      2.6030420         3.1020546
2     2.9158774      -0.64172468          -0.4014378  0.3936984  1.4654190   1.5919118     -0.5339425        -0.3019058
3    -0.3193230      -0.25899262          -0.1247284 -0.3861541 -0.6301142  -0.5435029     -0.2437014        -0.1136060
4    -0.4929087       0.01880576          -0.2897997 -0.3311455 -0.4862981  -0.5006112     -0.0987795        -0.3212159
5     0.4461372      -0.79381078          -0.5540657  3.0983433  1.4122942   0.7280222     -0.7453183        -0.5320357
 [1] 4 4 4 2 3 4 5 4 1 3 4 3
cluster_id_maker(cluster_123_6, table = cluster_table)

Four clusters with seed 4855

cluster_123_3 <- cluster_maker(seed = 4855, num_clusters = 4, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
2      -0.4672956  1.2357198           1.2354314            1.2354314                   1.1911790                   1.2741657   2.0971401  1.93751241
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.4093018 -0.35327890
4      -0.4226318 -0.6165979          -0.6196555           -0.6196555                  -0.6238333                  -0.6481908  -0.4183983 -0.48193274
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1     0.5534067       2.09967875           3.0684868 -0.3467066  1.9304181   2.3142417      2.6030420         3.1020546
2     1.6810073      -0.71776773          -0.4777518  1.7460208  1.4388566   1.1599670     -0.6396304        -0.4169707
3    -0.3193230      -0.25899262          -0.1247284 -0.3861541 -0.6301142  -0.5435029     -0.2437014        -0.1136060
4    -0.4929087       0.01880576          -0.2897997 -0.3311455 -0.4862981  -0.5006112     -0.0987795        -0.3212159
 [1] 4 4 4 2 3 4 2 4 1 3 4 3
cluster_id_maker(cluster_123_3, table = cluster_table)

Six clusters with seed 4855

cluster_123_6 <- cluster_maker(seed = 4855, num_clusters = 6, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.5409910 -0.7980288          -0.7998764           -0.7998764                  -0.8566746                  -0.7686479  -0.4659280 -0.34870651
2      -0.3366538  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   1.6390693  2.76526795
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.4093018 -0.35327890
4      -0.3634522 -0.5258824          -0.5295450           -0.5295450                  -0.5074127                  -0.5879623  -0.3946334 -0.54854585
5      -0.5979374  0.3734033           0.3706973            0.3706973                   0.1671229                   0.3341520   2.5552108  1.10975687
6      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1    -0.4433693        1.2911568          0.07105937 -0.3861273 -0.3579270  -0.4728730      0.8212504        -0.1032739
2     2.9158774       -0.6417247         -0.40143781  0.3936984  1.4654190   1.5919118     -0.5339425        -0.3019058
3    -0.3193230       -0.2589926         -0.12472840 -0.3861541 -0.6301142  -0.5435029     -0.2437014        -0.1136060
4    -0.5176784       -0.6173697         -0.47022920 -0.3036546 -0.5504837  -0.5144803     -0.5587944        -0.4301868
5     0.4461372       -0.7938108         -0.55406573  3.0983433  1.4122942   0.7280222     -0.7453183        -0.5320357
6     0.5534067        2.0996788          3.06848680 -0.3467066  1.9304181   2.3142417      2.6030420         3.1020546
 [1] 4 4 1 2 3 4 5 4 6 3 1 3
cluster_id_maker(cluster_123_6, table = cluster_table)

LS0tCnRpdGxlOiAiUHJvamVjdCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIFRPRE8gIAojIHBhc3NhciB0YWJlbGFzIHByYSBjc3YodG9kYXM/KSwgcmVuYW1lIGJsYW5rIGhhc2h0YWcvbWVkaWEsIGp1bnRhciB2YWxvcmVzIGRlIHJlcGx5IHByYSBjbHVzdGVyPyAKCiMjIyBGb3IgUiBiZWdpbm5lcnMKTmV3IGNodW5rICpDdHJsK0FsdCtJKgoKRXhlY3V0ZSBjaHVuayAqQ3RybCtTaGlmdCtFbnRlcioKCkV4ZWN1dGUgYWxsIGNodW5rcyAqQ3RybCtBbHQrUioKCkhUTUwgcHJldmlldyAqQ3RybCtTaGlmdCtLKgoKIyBMaWJyYXJ5IHByZXBhcmF0aW9ucwoKYGBge3J9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoc3RhdHMpCmBgYAoKIyBEYXRhIEltcG9ydAoKYGBge3J9CmRhdGEgPC0gcmVhZC5jc3YoIn4vNHllYXIvMnNlbWVzdGVyL2R0SUkvQ1NWcy9IRUlzLmNzdiIsCiAgICAgICAgICAgICAgICAgY29sQ2xhc3NlcyA9IGModHdlZXRfaWQgPSAiY2hhcmFjdGVyIikpCgojIE1vZGlmeWluZyBjcmVhdGVkX2F0IHR5cGUgc28gdGhhdCBhdHRyaWJ1dGUgY2FuIGJlIHVzZWQgbW9yZSBlYXNpbHkgCmRhdGEkY3JlYXRlZF9hdCA8LSBhcy5QT1NJWGN0KGRhdGEkY3JlYXRlZF9hdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0PSAiJVktJW0tJWRUJUg6JU06JVMiLCB0ej0iVVRDIikKCiNWaWV3KGRhdGEpCnN1bW1hcnkoZGF0YSkKYGBgCgojIEluaXRpYWwgRGF0YSBQcmVwYXJhdGlvbgoKYGBge3J9CiMgQ291bnQgb2YgaG93IG1hbnkgZW50cmllcyBlYWNoIEhFSSBoYXMKbnVtYmVyX2ludGVyYWN0aW9ucyA8LSBkYXRhICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkKSAlPiUgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKbnVtYmVyX2ludGVyYWN0aW9ucwpgYGAKCiMgU2luY2UgY29tcGx1dGVuc2Ugb25seSBoYXMgMSBlbnRyeSB3ZSBjYW4ndCBsZWFybiBhbnl0aGluZyBmcm9tIGl0LCBzbyB3ZSByZW1vdmVkIGl0CgpgYGB7cn0KZGF0YSA8LSBkYXRhW2RhdGEkaWQgIT0gImNvbXBsdXRlbnNlLmNzdiIsIF0KYGBgCgojIFZpc3VhbGl6YXRpb24gb2YgbnVtYmVyIGFsbCBwb3N0cywganVzdCB0d2VldHMgYW5kIGp1c3QgcmVwbGllcwoKYGBge3J9Cm51bWJlcl9wb3N0cyA8LSBkYXRhICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkKSAlPiUgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKbnVtYmVyX3R3ZWV0cyA8LSBkYXRhW2RhdGEkdHlwZSA9PSAiVHdlZXQiLCBdICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkKSAlPiUgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKbnVtYmVyX3JlcGxpZXMgPC0gZGF0YVtkYXRhJHR5cGUgPT0gIlJlcGx5IiwgXSAlPiUKICAgICAgICAgICAgICBncm91cF9ieShpZCkgJT4lIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKCnByaW50KG51bWJlcl9wb3N0cykKcHJpbnQobnVtYmVyX3R3ZWV0cykKcHJpbnQobnVtYmVyX3JlcGxpZXMpCmBgYAoKIyBDYWxjdWxhdGluZyB0aGUgcGVyY2VudGFnZSBvZiB0d2VldHMgYW5kIHJlcGxpZXMgYmFzZWQgb24gYWxsIHBvc3RzCgpgYGB7cn0KIyBNZXJnaW5nIHRoZSBjb3VudHMgb2YgdHdlZXRzIChjb3VudC55KSBhbmQgcmVwbGllcyAoY291bnQpIHdpdGggdGhlIGNvdW50IG9mIHBvc3RzIChjb3VudC54KQpkYXRhX3JhdGlvIDwtIG1lcmdlKG51bWJlcl9wb3N0cywgbnVtYmVyX3R3ZWV0cywgYnkgPSAiaWQiLCBhbGwgPSBUUlVFKQpkYXRhX3JhdGlvIDwtIG1lcmdlKGRhdGFfcmF0aW8sIG51bWJlcl9yZXBsaWVzLCBieSA9ICJpZCIsIGFsbCA9IFRSVUUpCgoKZGF0YV9yYXRpbyRwZXJjZW50YWdlX3R3ZWV0cyA8LSAoZGF0YV9yYXRpbyRjb3VudC55IC8gZGF0YV9yYXRpbyRjb3VudC54KSAqIDEwMApkYXRhX3JhdGlvJHBlcmNlbnRhZ2VfcmVwbGllcyA8LSAoZGF0YV9yYXRpbyRjb3VudCAvIGRhdGFfcmF0aW8kY291bnQueCkgKiAxMDAKCmRhdGFfcmF0aW8gPC0gZGF0YV9yYXRpb1ssIGMoImlkIiwgInBlcmNlbnRhZ2VfdHdlZXRzIiwgInBlcmNlbnRhZ2VfcmVwbGllcyIpXQoKcHJpbnQoZGF0YV9yYXRpbykKYGBgCgojIE5BIHJlbW92YWwKCiMgRnVuY3Rpb24gdG8gdmlzdWFsaXplIHRoZSBudW1iZXIgb2YgTkFzIGluIGFsbCBjb2x1bW5zCgpgYGB7cn0KbmFfY291bnQgPC0gZnVuY3Rpb24oKXsKICAjIENvdW50aW5nIHRoZSBudW1iZXIgb2YgTkEgdmFsdWVzIGZvciBlYWNoIGNvbHVtbgogIG5hX2NvdW50IDwtIGNvbFN1bXMoaXMubmEoZGF0YSkpCiAgCiAgIyBDcmVhdGluZyBhIG5ldyBkYXRhIGZyYW1lIHdpdGggdGhlIE5BIGNvdW50cwogIG5hX2NvdW50c190YWJsZSA8LSBkYXRhLmZyYW1lKENvbHVtbiA9IG5hbWVzKG5hX2NvdW50KSwgTkFfQ291bnQgPSBuYV9jb3VudCkKICAKICBwcmludChuYV9jb3VudHNfdGFibGUpCn0KYGBgCgojIENhbGN1bGF0aW9ucyBvZiB2aWV3LCBmYXZvdXJpdGUsIHJldHdlZXQgYW5kIHJlcGx5IHBlcmNlbnRpbGVzIGFuZCB2aXN1YWxpemF0aW9uIG9mIE5BcyBpbiBhbGwgY29sdW1ucwoKYGBge3J9CmRhdGEgPC0gZGF0YSAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgbXV0YXRlKHZpZXdfcGVyY2VudGlsZSA9IG50aWxlKHZpZXdfY291bnQsIDEwMCksCiAgICAgICAgIGZhdm9yaXRlX3BlcmNlbnRpbGUgPSBudGlsZShmYXZvcml0ZV9jb3VudCwgMTAwKSwKICAgICAgICAgcmV0d2VldF9wZXJjZW50aWxlID0gbnRpbGUocmV0d2VldF9jb3VudCwgMTAwKSwKICAgICAgICAgcmVwbHlfcGVyY2VudGlsZSA9IG50aWxlKHJlcGx5X2NvdW50LCAxMDApKSAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKGF2Z19wZXJjZW50aWxlID0gbWVhbihjKHZpZXdfcGVyY2VudGlsZSwgZmF2b3JpdGVfcGVyY2VudGlsZSwgcmV0d2VldF9wZXJjZW50aWxlLCByZXBseV9wZXJjZW50aWxlKSwgbmEucm0gPSBUUlVFKSkKCm5hX2NvdW50KCkKYGBgCgojIENhbGN1bGF0aW9uIG9mIHRoZSBtYXhpbXVtIG51bWJlciBvZiB2aWV3cyBmb3IgZWFjaCBIRUkKCmBgYHtyfQptYXhfdmlld19jb3VudHMgPC0gdGFwcGx5KGRhdGEkdmlld19jb3VudCwgZGF0YSRpZCwgbWF4LCBuYS5ybSA9IFRSVUUpCmBgYAoKIyBSZW1vdmFsIG9mIE5BcwoKYGBge3J9CiMgRnJvbSB2aWV3IGNvdW50CmRhdGEkdmlld19jb3VudCA8LSBpZmVsc2UoCiAgaXMubmEoZGF0YSR2aWV3X2NvdW50KSwKICByb3VuZChtYXhfdmlld19jb3VudHNbZGF0YSRpZF0gKiAoZGF0YSRhdmdfcGVyY2VudGlsZSAvIDEwMCkpLAogIGRhdGEkdmlld19jb3VudCkKCiMgRnJvbSB2aWV3IHBlcmNlbnRpbGUKZGF0YSR2aWV3X3BlcmNlbnRpbGUgPC0gaWZlbHNlKAogIGlzLm5hKGRhdGEkdmlld19wZXJjZW50aWxlKSwKICBkYXRhJGF2Z19wZXJjZW50aWxlLAogIGRhdGEkdmlld19wZXJjZW50aWxlKQpgYGAKCiMgVmlzdWFsaXphdGlvbiBvZiBOQXMgaW4gYWxsIGNvbHVtbnMKCmBgYHtyfQpuYV9jb3VudCgpCmBgYAoKIyBGb3Igbm93IHdlJ2xsIGJlIG9ubHkgbG9va2luZyBhdCB0d2VldHMKCmBgYHtyfQpkYXRhX3R3ZWV0cyA8LSBkYXRhW2RhdGEkdHlwZSA9PSAiVHdlZXQiLCBdCgpkYXRhX3R3ZWV0cwpgYGAKCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIGF2ZXJhZ2UgcG9zdHMKCmBgYHtyfQphdmVyYWdlX3R3ZWV0cyA8LSBmdW5jdGlvbih0aW1lZnJhbWUgPSAiZGF5cyIpewogICMgQ2FsY3VsYXRpb24gb2YgdGhlIHRpbWVmcmFtZSBiZXR3ZWVuIGVhcmxpZXN0IGFuZCBsYXRlc3QgcG9zdCBmb3IgZWFjaCBIRUkKICBkYXRlX3JhbmdlIDwtIGRhdGFfdHdlZXRzICU+JQogICAgZ3JvdXBfYnkoaWQpICU+JQogICAgc3VtbWFyaXNlKG1pbl9kYXRlID0gbWluKGNyZWF0ZWRfYXQpLAogICAgICAgICAgICAgIG1heF9kYXRlID0gbWF4KGNyZWF0ZWRfYXQpKSAlPiUKICAgIG11dGF0ZShudW1fZGF5cyA9IGFzLm51bWVyaWMoZGlmZnRpbWUobWF4X2RhdGUsIG1pbl9kYXRlLCB1bml0cyA9IHRpbWVmcmFtZSkpKQogIAogICMgTmFtaW5nIHRoZSBjb2x1bW4gcmVzcGVjdGluZyB0aGUgdGltZWZyYW1lCiAgY29sdW1uX25hbWUgPC0gcGFzdGUwKCJhdmdfdHdlZXRzX3Blcl8iLCB0aW1lZnJhbWUpCiAgCiAgIyBDYWxjdWxhdGlvbiBvZiB0aGUgbnVtYmVyIG9mIHR3ZWV0cyBwZXIgZGF5IGZvciBlYWNoIEhFSQogIHR3ZWV0c19wZXJfdGltZWZyYW1lIDwtIG51bWJlcl90d2VldHMgJT4lCiAgICBsZWZ0X2pvaW4oZGF0ZV9yYW5nZSwgYnkgPSAiaWQiKSAlPiUKICAgIG11dGF0ZSghIWNvbHVtbl9uYW1lIDo9IGNvdW50IC8gbnVtX2RheXMpCiAgCiAgcHJpbnQodHdlZXRzX3Blcl90aW1lZnJhbWUpCiAgcmV0dXJuKHR3ZWV0c19wZXJfdGltZWZyYW1lKQp9CmBgYAoKYGBge3J9CnR3ZWV0c19wZXJfZGF5IDwtIGF2ZXJhZ2VfdHdlZXRzKCkKdHdlZXRzX3Blcl93ZWVrIDwtIGF2ZXJhZ2VfdHdlZXRzKHRpbWVmcmFtZSA9ICJ3ZWVrcyIpCmBgYAoKIyBQbG90IGZvciB0aGUgYXZlcmFnZSBudW1iZXIgb2YgdHdlZXRzIHBlciBkYXkgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdCh0d2VldHNfcGVyX2RheSRhdmdfdHdlZXRzX3Blcl9kYXlzLAogICAgICAgIG5hbWVzLmFyZyA9IHR3ZWV0c19wZXJfZGF5JGlkLAogICAgICAgIG1haW4gPSAiQXZlcmFnZSBUd2VldHMgcGVyIERheSIsCiAgICAgICAgeGxhYiA9ICJIRUkiLAogICAgICAgIHlsYWIgPSAiQXZlcmFnZSBOdW1iZXIgb2YgVHdlZXRzIiwKICAgICAgICB5bGltID0gYygwLCBtYXgodHdlZXRzX3Blcl9kYXkkYXZnX3R3ZWV0c19wZXJfZGF5cykgKyAxKSwKICAgICAgICBsYXMgPSAyLAogICAgICAgIGNvbCA9ICIjMzQ5OERCIikKCiMgQWRkaW5nIHRleHQgbGFiZWxzIG92ZXIgZWFjaCBiYXIgYW5kIGFsaWduaW5nIGl0IHdpdGggdGhlIGNlbnRlciBvZiBlYWNoIGJhciAKdGV4dCh4ID0gYmFycGxvdCh0d2VldHNfcGVyX2RheSRhdmdfdHdlZXRzX3Blcl9kYXlzLCBwbG90ID0gRkFMU0UpLAogICAgIHkgPSB0d2VldHNfcGVyX2RheSRhdmdfdHdlZXRzX3Blcl9kYXlzLAogICAgIGxhYmVscyA9IHJvdW5kKHR3ZWV0c19wZXJfZGF5JGF2Z190d2VldHNfcGVyX2RheXMsIDIpLAogICAgIHBvcyA9IDMpCmBgYAoKIyBQbG90IGZvciB0aGUgYXZlcmFnZSBudW1iZXIgb2YgdHdlZXRzIHBlciB3ZWVrIGZvciBlYWNoIEhFSQoKYGBge3J9CmJhcnBsb3QodHdlZXRzX3Blcl93ZWVrJGF2Z190d2VldHNfcGVyX3dlZWtzLAogICAgICAgIG5hbWVzLmFyZyA9IHR3ZWV0c19wZXJfd2VlayRpZCwKICAgICAgICBtYWluID0gIkF2ZXJhZ2UgVHdlZXRzIHBlciBXZWVrIiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBUd2VldHMiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heCh0d2VldHNfcGVyX3dlZWskYXZnX3R3ZWV0c19wZXJfd2Vla3MpICsgNSksCiAgICAgICAgbGFzID0gMiwKICAgICAgICBjb2wgPSAiI0U3NEMzQyIpCgp0ZXh0KHggPSBiYXJwbG90KHR3ZWV0c19wZXJfd2VlayRhdmdfdHdlZXRzX3Blcl93ZWVrcywgcGxvdCA9IEZBTFNFKSwKICAgICB5ID0gdHdlZXRzX3Blcl93ZWVrJGF2Z190d2VldHNfcGVyX3dlZWtzLAogICAgIGxhYmVscyA9IHJvdW5kKHR3ZWV0c19wZXJfd2VlayRhdmdfdHdlZXRzX3Blcl93ZWVrcywgMiksCiAgICAgcG9zID0gMykKYGBgCgojIERlZmluaW5nIHRoZSBpbnRlcnZhbHMgb2YgdGltZSBmb3IgdGhlIGFjYWRlbWljIHllYXIKCmBgYHtyfQppbnRlcnZhbHMgPC0gbGlzdCgKICBpbnRlcnZhbDEgPSBhcy5QT1NJWGN0KGMoIjIwMjItMDgtMzEiLCAiMjAyMi0xMi0xNSIpKSwKICBpbnRlcnZhbDIgPSBhcy5QT1NJWGN0KGMoIjIwMjMtMDEtMDQiLCAiMjAyMy0wNC0wMSIpKSwKICBpbnRlcnZhbDMgPSBhcy5QT1NJWGN0KGMoIjIwMjMtMDQtMTQiLCAiMjAyMy0wNi0xNSIpKQopCmBgYAoKIyBGdW5jdGlvbiB0byBjaGVjayBpZiBhIGRhdGUgZmFsbHMgd2l0aGluIGEgZ2l2ZW4gaW50ZXJ2YWwgb2YgdGltZSBhbmQgYXBwbHkgYXBwcm9wcmlhdGUgQm9vbGVhbgoKYGBge3J9CmNoZWNrX2ludGVydmFsIDwtIGZ1bmN0aW9uKGRhdGUpIHsKICBmb3IgKGkgaW4gMTpsZW5ndGgoaW50ZXJ2YWxzKSkgewogICAgaW50ZXJ2YWxfc3RhcnQgPC0gaW50ZXJ2YWxzW1tpXV1bMV0KICAgIGludGVydmFsX2VuZCA8LSBpbnRlcnZhbHNbW2ldXVsyXQogICAgaWYgKGRhdGUgPj0gaW50ZXJ2YWxfc3RhcnQgJiBkYXRlIDw9IGludGVydmFsX2VuZCkgewogICAgICByZXR1cm4oVFJVRSkKICAgIH0KICB9CiAgcmV0dXJuKEZBTFNFKQp9CmBgYAoKYGBge3J9CmRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIgPC0gc2FwcGx5KGRhdGFfdHdlZXRzJGNyZWF0ZWRfYXQsIGNoZWNrX2ludGVydmFsKQpwcmludChkYXRhLmZyYW1lKGlkID0gZGF0YV90d2VldHMkaWQsIGFjYWRlbWljX3llYXIgPSBkYXRhX3R3ZWV0cyRhY2FkZW1pY195ZWFyKSkKYGBgCgojIFBsb3QgZm9yIHRoZSBudW1iZXIgb2YgdHdlZXRzIHBlciB0aW1lZnJhbWUgb2YgZWl0aGVyIHZhY2F0aW9uIG9yIGFjYWRlbWljIHRpbWUKCmBgYHtyfQpiYXJwbG90KHRhYmxlKGRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIpLAogICAgICAgIG1haW4gPSAiTnVtYmVyIG9mIFR3ZWV0cyBwZXIgVGltZWZyYW1lIiwKICAgICAgICB4bGFiID0gIlRpbWUiLAogICAgICAgIHlsYWIgPSAiQ291bnQiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heCh0YWJsZShkYXRhX3R3ZWV0cyRhY2FkZW1pY195ZWFyKSkgKyAxMDAwKSwKICAgICAgICBuYW1lcy5hcmcgPSBjKCJWYWNhdGlvbiIsICJBY2FkZW1pYyIpLAogICAgICAgIGNvbCA9IGMoIiM4RTQ0QUQiLCAiI0YxQzQwRiIpKQoKdGV4dCh4ID0gYmFycGxvdChkYXRhX3R3ZWV0cyRhY2FkZW1pY195ZWFyLCBwbG90ID0gRkFMU0UpLCAKICAgICB5ID0gdGFibGUoZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhcikgKyAwLjUsIAogICAgIGxhYmVscyA9IHRhYmxlKGRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIpLCAKICAgICBwb3MgPSAzKQpgYGAKCiMgRnVuY3Rpb24gdG8gY291bnQgbnVtYmVyIG9mIHR3ZWV0cyBhbmQgYXZlcmFnZSBwZXIgZGF5CgpgYGB7cn0KYW5hbHl6ZV90d2VldHMgPC0gZnVuY3Rpb24oYWNhZGVtaWNfeWVhcl9maWx0ZXIgPSBUUlVFKSB7CiAgIyBGaWx0ZXJpbmcgdGhlIGRhdGEgYmFzZWQgb24gdGhlIGFjYWRlbWljX3llYXJfZmlsdGVyCiAgZmlsdGVyZWRfZGF0YSA8LSBkYXRhX3R3ZWV0cyAlPiUKICAgIGZpbHRlcihhY2FkZW1pY195ZWFyID09IGFjYWRlbWljX3llYXJfZmlsdGVyKQogIAogICMgQ291bnQgb2YgZGF5cyBmb3IgZWFjaCBIRUkKICB1bmlxdWVfZGF5cyA8LSBmaWx0ZXJlZF9kYXRhICU+JQogICAgZ3JvdXBfYnkoaWQpICU+JQogICAgc3VtbWFyaXNlKHVuaXF1ZV9kYXlzID0gbl9kaXN0aW5jdChhcy5EYXRlKGNyZWF0ZWRfYXQpKSkKICAKICAjIENvdW50IG9mIHR3ZWV0cyBmb3IgZWFjaCBIRUkKICBudW1iZXJfdHdlZXRzX2Jvb2xlYW4gPC0gZmlsdGVyZWRfZGF0YSAlPiUKICAgIGdyb3VwX2J5KGlkKSAlPiUKICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKICAKICAjIE5hbWluZyB0aGUgY29sdW1uIHJlc3BlY3RpbmcgdGhlIHRpbWUgcGVyaW9kCiAgeWVhciA8LSBpZmVsc2UoYWNhZGVtaWNfeWVhcl9maWx0ZXIsICJhY2FkZW1pY190aW1lIiwgInZhY2F0aW9uX3RpbWUiKQogIGNvbHVtbl9uYW1lIDwtIHBhc3RlMCgiYXZnX3R3ZWV0c19pbl8iLCB5ZWFyKQogIAogICMgQ29tYmluYXRpb24gb2YgZGF0YSBhbmQgY2FsY3VsYXRpb24gb2YgYXZlcmFnZSBwb3N0cyBwZXIgZGF5CiAgY29tYmluZWRfZGF0YSA8LSBsZWZ0X2pvaW4odW5pcXVlX2RheXMsIG51bWJlcl90d2VldHNfYm9vbGVhbiwgYnkgPSAiaWQiKQogIGNvbWJpbmVkX2RhdGEgPC0gY29tYmluZWRfZGF0YSAlPiUKICAgIG11dGF0ZSghIWNvbHVtbl9uYW1lIDo9IGNvdW50IC8gdW5pcXVlX2RheXMpCiAgCiAgcHJpbnQoY29tYmluZWRfZGF0YSkKICByZXR1cm4oY29tYmluZWRfZGF0YSkKfQpgYGAKCmBgYHtyfQpkYXRhX3R3ZWV0c19hY2FkZW1pYyA8LSBhbmFseXplX3R3ZWV0cygpCmRhdGFfdHdlZXRzX3ZhY2F0aW9ucyA8LSBhbmFseXplX3R3ZWV0cyhhY2FkZW1pY195ZWFyX2ZpbHRlciA9IEZBTFNFKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHR3ZWV0cyBkdXJpbmcgYWNhZGVtaWMgdGltZSBmb3IgZWFjaCBIRUkKCmBgYHtyfQpiYXJwbG90KGRhdGFfdHdlZXRzX2FjYWRlbWljJGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSwKICAgICAgICBuYW1lcy5hcmcgPSBkYXRhX3R3ZWV0c19hY2FkZW1pYyRpZCwKICAgICAgICBtYWluID0gIkF2ZXJhZ2UgVHdlZXRzIGR1cmluZyBBY2FkZW1pYyBUaW1lIiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBUd2VldHMiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heChkYXRhX3R3ZWV0c19hY2FkZW1pYyRhdmdfdHdlZXRzX2luX2FjYWRlbWljX3RpbWUpICsgNSksCiAgICAgICAgbGFzID0gMiwKICAgICAgICBjb2wgPSAiIzM0NDk1RSIpCgp0ZXh0KHggPSBiYXJwbG90KGRhdGFfdHdlZXRzX2FjYWRlbWljJGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSwgcGxvdCA9IEZBTFNFKSwKICAgICB5ID0gZGF0YV90d2VldHNfYWNhZGVtaWMkYXZnX3R3ZWV0c19pbl9hY2FkZW1pY190aW1lLAogICAgIGxhYmVscyA9IHJvdW5kKGRhdGFfdHdlZXRzX2FjYWRlbWljJGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSwgMiksCiAgICAgcG9zID0gMykKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0d2VldHMgZHVyaW5nIHZhY2F0aW9uIHRpbWUgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdChkYXRhX3R3ZWV0c192YWNhdGlvbnMkYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lLAogICAgICAgIG5hbWVzLmFyZyA9IGRhdGFfdHdlZXRzX3ZhY2F0aW9ucyRpZCwKICAgICAgICBtYWluID0gIkF2ZXJhZ2UgVHdlZXRzIGR1cmluZyBWYWNhdGlvbiBUaW1lIiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBUd2VldHMiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heChkYXRhX3R3ZWV0c192YWNhdGlvbnMkYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lKSArIDUpLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sID0gIiNEMzU0MDAiKQoKdGV4dCh4ID0gYmFycGxvdChkYXRhX3R3ZWV0c192YWNhdGlvbnMkYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lLCBwbG90ID0gRkFMU0UpLAogICAgIHkgPSBkYXRhX3R3ZWV0c192YWNhdGlvbnMkYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lLAogICAgIGxhYmVscyA9IHJvdW5kKGRhdGFfdHdlZXRzX3ZhY2F0aW9ucyRhdmdfdHdlZXRzX2luX3ZhY2F0aW9uX3RpbWUsIDIpLAogICAgIHBvcyA9IDMpCmBgYAoKIyBEYXRhIHByZXBhcmF0aW9uIGZvciBkYXkgb2YgdGhlIHdlZWsgCgpgYGB7cn0KIyBDcmVhdGluZyBuZXcgdGFibGUgdGhhdCBjb250YWlucyBhIG5ldyBjb2x1bW4gZm9yIHRoZSBkYXkgb2YgdGhlIHdlZWsKZGF0YV90d2VldHNfZGF5cyA8LSBkYXRhX3R3ZWV0cyAlPiUKICBtdXRhdGUoZGF5X29mX3dlZWsgPSB3ZWVrZGF5cyhjcmVhdGVkX2F0KSkKCiMgU2VsZWN0aW5nIG9ubHkgdGhlIGlkLCBjcmVhdGVkX2F0LCBhbmQgZGF5X29mX3dlZWsgY29sdW1ucyBmb3IgdGhlIG5ldyB0YWJsZQpkYXRhX3R3ZWV0c19kYXlzIDwtIGRhdGFfdHdlZXRzX2RheXMgJT4lCiAgc2VsZWN0KGlkLCBjcmVhdGVkX2F0LCBkYXlfb2Zfd2VlaykKCnByaW50KGRhdGFfdHdlZXRzX2RheXMpCmBgYAoKYGBge3J9CiMgR3JvdXBpbmcgYnkgaWQgYW5kIGRheV9vZl93ZWVrLCB0aGVuIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgdHdlZXRzCm51bWJlcl90d2VldHNfZGF5cyA8LSBkYXRhX3R3ZWV0c19kYXlzICU+JQogIGdyb3VwX2J5KGlkLCBkYXlfb2Zfd2VlaykgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKIyBHcm91cGluZyBieSBpZCwgZGF5X29mX3dlZWsgYW5kIGRheSBjcmVhdGVkIGF0LCB0aGVuIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgdHdlZXRzCm51bWJlcl90d2VldHNfcGVyX2RheSA8LSBkYXRhX3R3ZWV0c19kYXlzICU+JQogIG11dGF0ZShjcmVhdGVkX2RhdGUgPSBhcy5EYXRlKGNyZWF0ZWRfYXQpKSAlPiUKICBncm91cF9ieShpZCwgZGF5X29mX3dlZWssIGNyZWF0ZWRfZGF0ZSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKIyBGaW5kaW5nIGZvciBlYWNoIEhFSSB0aGUgYXZlcmFnZSBjb3VudCBvZiB0d2VldHMgcGVyIGRheQphdmVyYWdlX251bWJlcl90d2VldHNfcGVyX2RheSA8LSBudW1iZXJfdHdlZXRzX3Blcl9kYXkgJT4lCiAgZ3JvdXBfYnkoaWQsIGRheV9vZl93ZWVrKSAlPiUKICBzdW1tYXJpc2UoYXZlcmFnZV9jb3VudCA9IG1lYW4oY291bnQpKQoKcHJpbnQobnVtYmVyX3R3ZWV0c19kYXlzKQpgYGAKCiMgSGlnaGVzdCBhbmQgbG93ZXN0IHR3ZWV0cwoKYGBge3J9CiMgRmluZGluZyB0aGUgSEVJIHdpdGggdGhlIGxvd2VzdCBjb3VudCBvZiB0d2VldHMgcGVyIGRheQpsb3dlc3RfY291bnQgPC0gbnVtYmVyX3R3ZWV0c19kYXlzICU+JQogIGdyb3VwX2J5KGRheV9vZl93ZWVrKSAlPiUKICBzbGljZV9taW4ob3JkZXJfYnkgPSBjb3VudCkgJT4lCiAgc2VsZWN0KGRheV9vZl93ZWVrLCBpZCwgY291bnQpCgojIEZpbmRpbmcgdGhlIEhFSSB3aXRoIHRoZSBoaWdoZXN0IGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5CmhpZ2hlc3RfY291bnQgPC0gbnVtYmVyX3R3ZWV0c19kYXlzICU+JQogIGdyb3VwX2J5KGRheV9vZl93ZWVrKSAlPiUKICBzbGljZV9tYXgob3JkZXJfYnkgPSBjb3VudCkgJT4lCiAgc2VsZWN0KGRheV9vZl93ZWVrLCBpZCwgY291bnQpCgojIENvbWJpbmUgdGhlIHJlc3VsdHMKaGlnaF9sb3dfSEVJIDwtIGJpbmRfcm93cyhsb3dlc3RfY291bnQsIGhpZ2hlc3RfY291bnQpICU+JQogIGFycmFuZ2UoZGF5X29mX3dlZWspCgpwcmludChoaWdoX2xvd19IRUkpCmBgYAoKIyBQbG90IGZvciB0aGUgbG93ZXN0IGFuZCBoaWdoZXN0IGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5IGZvciBlYWNoIGRheSBvZiB0aGUgd2VlawoKYGBge3J9CmdncGxvdChoaWdoX2xvd19IRUksIGFlcyh4ID0gZGF5X29mX3dlZWssIHkgPSBjb3VudCwgZmlsbCA9IGlkKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY291bnQpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LAogICAgICAgICAgICBzaXplID0gMykgKwogIGxhYnModGl0bGUgPSAiTG93ZXN0IGFuZCBIaWdoZXN0IENvdW50IG9mIFR3ZWV0cyBwZXIgRGF5IGZvciBFYWNoIERheSBvZiB0aGUgV2VlayIsCiAgICAgICB4ID0gIkRheSBvZiB0aGUgV2VlayIsIHkgPSAiQ291bnQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcmFpbmJvdyhsZW5ndGgodW5pcXVlKGhpZ2hfbG93X0hFSSRpZCkpKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMgQXZlcmFnZSBvZiB0d2VldHMKCmBgYHtyfQojIEZpbmRpbmcgdGhlIEhFSSB3aXRoIGxvd2VzdCBhbmQgaGlnaGVzdCBhdmVyYWdlZCBjb3VudCBvZiB0d2VldHMgcGVyIGRheQpoaWdoX2xvd19hdmVyYWdlX0hFSXMgPC0gYXZlcmFnZV9udW1iZXJfdHdlZXRzX3Blcl9kYXkgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3dlZWspICU+JQogIGZpbHRlcihhdmVyYWdlX2NvdW50ID09IG1heChhdmVyYWdlX2NvdW50KSB8IGF2ZXJhZ2VfY291bnQgPT0gbWluKGF2ZXJhZ2VfY291bnQpKSAlPiUKICBhcnJhbmdlKGRheV9vZl93ZWVrLCBpZmVsc2UoYXZlcmFnZV9jb3VudCA9PSBtaW4oYXZlcmFnZV9jb3VudCksIGF2ZXJhZ2VfY291bnQsIC1hdmVyYWdlX2NvdW50KSkKCnByaW50KGhpZ2hfbG93X2F2ZXJhZ2VfSEVJcykKYGBgCgojIFBsb3QgZm9yIHRoZSBoaWdoZXN0IGFuZCBsb3dlc3QgYXZlcmFnZSBjb3VudCBvZiB0d2VldHMgcGVyIGRheSBmb3IgZWFjaCBkYXkgb2YgdGhlIHdlZWsKCmBgYHtyfQpnZ3Bsb3QoaGlnaF9sb3dfYXZlcmFnZV9IRUlzLCBhZXMoeCA9IGRheV9vZl93ZWVrLCB5ID0gYXZlcmFnZV9jb3VudCwgZmlsbCA9IGlkKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoYXZlcmFnZV9jb3VudCwgMikpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC43KSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LAogICAgICAgICAgICBzaXplID0gMykgKwogIGxhYnModGl0bGUgPSAiSGlnaGVzdCBhbmQgTG93ZXN0IEF2ZXJhZ2UgQ291bnQgb2YgVHdlZXRzIHBlciBEYXkgZm9yIEVhY2ggRGF5IG9mIHRoZSBXZWVrIiwKICAgICAgIHggPSAiRGF5IG9mIHRoZSBXZWVrIiwgeSA9ICJBdmVyYWdlIENvdW50IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHJhaW5ib3cobGVuZ3RoKHVuaXF1ZShoaWdoX2xvd19IRUkkaWQpKSkpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCiMgVmlld3MgTGlrZXMgUmV0d2VldHMgYW5kIFJlcGxpZXMKCmBgYHtyfQojIFRhYmxlIGNvbnRhaW5pbmcgdmlld3MsIGxpa2VzLCByZXR3ZWV0cyBhbmQgcmVwbGllcyBmb3IgZWFjaCBtZWRpYSB0eXBlIGZvciBlYWNoIEhFSQp0eXBlc19vZl90d2VldHMgPC0gZGF0YV90d2VldHMgJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQsIG1lZGlhX3R5cGUpICU+JQogICAgICAgICAgICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgICAgICAgICAgICAgdmlld3MgPSBzdW0odmlld19jb3VudCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgbGlrZXMgPSBzdW0oZmF2b3JpdGVfY291bnQsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgIHJldHdlZXRzID0gc3VtKHJldHdlZXRfY291bnQsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxpZXMgPSBzdW0ocmVwbHlfY291bnQsIG5hLnJtID0gVFJVRSkpCiAgICAgICAgICAgICAgICAgICAgICAgIApwcmludCh0eXBlc19vZl90d2VldHMpICAgICAgICAgICAgICAgICAgICAgICAgCmBgYAoKYGBge3J9CiMgR3JvdXBpbmcgYnkgSEVJIGFuZCBjYWxjdWxhdGluZyB0aGUgdG90YWwgdmFsdWVzIG9mIHZpZXdzLCBsaWtlcyBhbmQgcmVwbGllcyBhY3Jvc3MgYWxsIG1lZGlhIHR5cGVzCnRvdGFsX3R3ZWV0c19zdGF0cyA8LSB0eXBlc19vZl90d2VldHMgJT4lCiAgZ3JvdXBfYnkoaWQpICU+JQogIHN1bW1hcmlzZSh0b3RhbF92aWV3cyA9IHN1bSh2aWV3cyksCiAgICAgICAgICAgIHRvdGFsX2xpa2VzID0gc3VtKGxpa2VzKSwKICAgICAgICAgICAgdG90YWxfcmVwbGllcyA9IHN1bShyZXBsaWVzKSkKCnByaW50KHRvdGFsX3R3ZWV0c19zdGF0cykKYGBgCgojIEZ1bmN0aW9uIGZvciBwaWVjaGFydCBjcmVhdGlvbiBmb3Igdmlld3MsIGxpa2VzIGFuZCByZXBsaWVzIAoKYGBge3J9CnBpZV9tYWtlciA8LSBmdW5jdGlvbih0YXJnZXRfaWQgPSAiZHVrZS5jc3YiKXsKICAjIEZpbHRlcmluZyBkYXRhIGZvciB0aGUgc3BlY2lmaWMgSEVJCiAgaGVpX2RhdGEgPC0gdHlwZXNfb2ZfdHdlZXRzICU+JQogICAgZmlsdGVyKGlkID09IHRhcmdldF9pZCkKICAKICAjIENhbGN1bGF0aW5nIHRvdGFsIHZpZXdzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIHRoZSBzcGVjaWZpYyBIRUkKICBoZWlfbWVkaWEgPC0gaGVpX2RhdGEgJT4lCiAgICBncm91cF9ieShtZWRpYV90eXBlKSAlPiUKICAgIHN1bW1hcmlzZSh0b3RhbF92aWV3cyA9IHN1bSh2aWV3cyksCiAgICAgICAgICAgICAgdG90YWxfbGlrZXMgPSBzdW0obGlrZXMpLAogICAgICAgICAgICAgIHRvdGFsX3JlcGxpZXMgPSBzdW0ocmVwbGllcykpCiAgCiAgIyBDYWxjdWxhdGluZyB0aGUgcGVyY2VudGFnZSBvZiB2aWV3cyBmb3IgZWFjaCBtZWRpYSB0eXBlIGZvciB0aGUgc3BlY2lmaWMgSEVJCiAgaGVpX21lZGlhJHBlcmNlbnRhZ2VfdmlldyA8LSBoZWlfbWVkaWEkdG90YWxfdmlld3MgLyBzdW0oaGVpX21lZGlhJHRvdGFsX3ZpZXdzKSAqIDEwMAogIGhlaV9tZWRpYSRwZXJjZW50YWdlX2xpa2UgPC0gaGVpX21lZGlhJHRvdGFsX2xpa2VzIC8gc3VtKGhlaV9tZWRpYSR0b3RhbF9saWtlcykgKiAxMDAKICBoZWlfbWVkaWEkcGVyY2VudGFnZV9yZXBseSA8LSBoZWlfbWVkaWEkdG90YWxfcmVwbGllcyAvIHN1bShoZWlfbWVkaWEkdG90YWxfcmVwbGllcykgKiAxMDAKICAKICAjIENyZWF0aW5nIHRoZSBwaWUgY2hhcnQgZm9yIHZpZXdzCiAgaGVpX3BpZV9jaGFydF92aWV3cyA8LSBnZ3Bsb3QoaGVpX21lZGlhLCBhZXMoeCA9ICIiLCB5ID0gcGVyY2VudGFnZV92aWV3LCBmaWxsID0gbWVkaWFfdHlwZSkpICsKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsKICAgIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQgPSAwKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlKG1lZGlhX3R5cGUsICJcbiIsIHRvdGFsX3ZpZXdzLCAiKCIsIHJvdW5kKHBlcmNlbnRhZ2VfdmlldywgMSksICIlKSIpKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIGNvbG9yID0gIiNGRkZGRkYiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJub19tZWRpYSIgPSAiIzIxOTZGMyIsICJhbmltYXRlZF9naWYiID0gIiNFNjdFMjIiLCAicGhvdG8iID0gIiM4RTQ0QUQiLCAidmlkZW8iID0gIiMxMzhENzUiKSkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJWaWV3cyBmb3IgZWFjaCBtZWRpYSB0eXBlIC0iLCB0YXJnZXRfaWQpKQogIAogICMgQ3JlYXRpbmcgdGhlIHBpZSBjaGFydCBmb3IgbGlrZXMKICBoZWlfcGllX2NoYXJ0X2xpa2VzIDwtIGdncGxvdChoZWlfbWVkaWEsIGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50YWdlX2xpa2UsIGZpbGwgPSBtZWRpYV90eXBlKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKwogICAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUobWVkaWFfdHlwZSwgIlxuIiwgdG90YWxfbGlrZXMsICIoIiwgcm91bmQocGVyY2VudGFnZV9saWtlLCAxKSwgIiUpIikpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgY29sb3IgPSAiI0ZGRkZGRiIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIm5vX21lZGlhIiA9ICIjRTkxRTYzIiwgImFuaW1hdGVkX2dpZiIgPSAiIzRBMTQ4QyIsICJwaG90byIgPSAiIzkwQ0FGOSIsICJ2aWRlbyIgPSAiIzAwQkZBNSIpKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIkxpa2VzIGZvciBlYWNoIG1lZGlhIHR5cGUgLSIsIHRhcmdldF9pZCkpCiAgCiAgIyBDcmVhdGluZyB0aGUgcGllIGNoYXJ0IGZvciByZXBsaWVzCiAgaGVpX3BpZV9jaGFydF9yZXBsaWVzIDwtIGdncGxvdChoZWlfbWVkaWEsIGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50YWdlX3JlcGx5LCBmaWxsID0gbWVkaWFfdHlwZSkpICsKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsKICAgIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQgPSAwKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlKG1lZGlhX3R5cGUsICJcbiIsIHRvdGFsX3JlcGxpZXMsICIoIiwgcm91bmQocGVyY2VudGFnZV9yZXBseSwgMSksICIlKSIpKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIGNvbG9yID0gIiNGRkZGRkYiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJub19tZWRpYSIgPSAiIzY2NjYwMCIsICJhbmltYXRlZF9naWYiID0gIiM5OUNDQ0MiLCAicGhvdG8iID0gIiM5OTY2Q0MiLCAidmlkZW8iID0gIiMzMzAwMDAiKSkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJSZXBsaWVzIGZvciBlYWNoIG1lZGlhIHR5cGUgLSIsIHRhcmdldF9pZCkpCiAgCiAgIyBQcmludCB0aGUgcGllIGNoYXJ0cwogIHByaW50KGhlaV9waWVfY2hhcnRfdmlld3MpCiAgcHJpbnQoaGVpX3BpZV9jaGFydF9saWtlcykKICBwcmludChoZWlfcGllX2NoYXJ0X3JlcGxpZXMpCn0KYGBgCgojIFBsb3Qgb2YgcGllY2hhcnRzIGZvciBlYWNoIEhFSQoKYGBge3J9CnBpZV9tYWtlcigpCnBpZV9tYWtlcigiZXBmbC5jc3YiKQpwaWVfbWFrZXIoImdvZS5jc3YiKQpwaWVfbWFrZXIoImhhcnZhcmQuY3N2IikKcGllX21ha2VyKCJsZWljZXN0ZXIuY3N2IikKcGllX21ha2VyKCJtYW5jaGVzdGVyLmNzdiIpCnBpZV9tYWtlcigibWl0LmNzdiIpCnBpZV9tYWtlcigic2IuY3N2IikKcGllX21ha2VyKCJzdGFuZm9yZC5jc3YiKQpwaWVfbWFrZXIoInRyaW5pdHkuY3N2IikKcGllX21ha2VyKCJ3di5jc3YiKQpwaWVfbWFrZXIoInlhbGUuY3N2IikKYGBgCgpgYGB7cn0KIyBDYWxjdWxhdGlvbiBvZiBsaWtlX3JhdGlvIGFuZCByZXBsaWVzX3JhdGlvIHBlcmNlbnRhZ2VzCnJhdGlvc190d2VldHNfdGFibGUgPC0gdG90YWxfdHdlZXRzX3N0YXRzICU+JQogIG11dGF0ZShsaWtlX3JhdGlvID0gdG90YWxfbGlrZXMgLyB0b3RhbF92aWV3cyAqIDEwMCwKICAgICAgICAgcmVwbGllc19yYXRpbyA9IHRvdGFsX3JlcGxpZXMgLyB0b3RhbF92aWV3cyAqIDEwMCkKCiMgQ3JlYXRpb24gb2YgbmV3IHRhYmxlIHdpdGggZWFjaCBIRUksIGxpa2VfcmF0aW8sIGFuZCByZXBsaWVzX3JhdGlvIApoZWlfdHdlZXRzX3JhdGlvcyA8LSByYXRpb3NfdHdlZXRzX3RhYmxlICU+JQogIHNlbGVjdChpZCwgbGlrZV9yYXRpbywgcmVwbGllc19yYXRpbykgJT4lCiAgZGlzdGluY3QoKQoKcHJpbnQoaGVpX3R3ZWV0c19yYXRpb3MpCmBgYAoKIyBQbG90IGZvciBsaWtlX3JhdGlvIGFuZCByZXBsaWVzX3JhdGlvIGZvciBlYWNoIEhFSQoKYGBge3J9CmdncGxvdChoZWlfdHdlZXRzX3JhdGlvcywgYWVzKHggPSBpZCkpICsKICBnZW9tX2JhcihhZXMoeSA9IGxpa2VfcmF0aW8sIGZpbGwgPSAiTGlrZSBSYXRpbyIpLCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSByZXBsaWVzX3JhdGlvLCBmaWxsID0gIlJlcGx5cyBSYXRpbyIpLCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gbGlrZV9yYXRpbywgbGFiZWwgPSByb3VuZChsaWtlX3JhdGlvLCAyKSksIHZqdXN0ID0gLTAuNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHNpemUgPSAzLCBjb2xvciA9ICIjMDAwMDAwIikgKwogIGdlb21fdGV4dChhZXMoeSA9IHJlcGxpZXNfcmF0aW8sIGxhYmVsID0gcm91bmQocmVwbGllc19yYXRpbywgMikpLCB2anVzdCA9IC0wLjUsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCBzaXplID0gMywgY29sb3IgPSAiI0ZGRkZGRiIpICsKICBsYWJzKHRpdGxlID0gIkxpa2UgYW5kIFJlcGx5cyBSYXRpb3MgYnkgSEVJIiwKICAgICAgIHggPSAiSEVJIiwKICAgICAgIHkgPSAiUmF0aW8gKCUpIiwKICAgICAgIGZpbGwgPSAiTWV0cmljIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkxpa2UgUmF0aW8iID0gIiMyMTk2RjMiLCAiUmVwbHlzIFJhdGlvIiA9ICIjRjQ0MzM2IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKQpgYGAKCmBgYHtyfQojIFRhYmxlIHdpdGggYXZlcmFnZXMgb2Ygdmlld3MsIGxpa2VzLCByZXR3ZWV0cyBhbmQgcmVwbGllcwp0eXBlc19vZl90d2VldHNfcGVyX3R3ZWV0IDwtIHR5cGVzX29mX3R3ZWV0cyAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQsIG1lZGlhX3R5cGUpICU+JQogICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UoYXZnX3ZpZXdzID0gbWVhbih2aWV3cyAvIGNvdW50KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF2Z19saWtlcyA9IG1lYW4obGlrZXMgLyBjb3VudCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdmdfcmV0d2VldHMgPSBtZWFuKHJldHdlZXRzIC8gY291bnQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXZnX3JlcGxpZXMgPSBtZWFuKHJlcGxpZXMgLyBjb3VudCkpCgpwcmludCh0eXBlc19vZl90d2VldHNfcGVyX3R3ZWV0KQpgYGAKCmBgYHtyfQojIEdyb3VwaW5nIGJ5IEhFSSBhbmQgY2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2UgdmFsdWVzIG9mIHZpZXdzLCBsaWtlcyBhbmQgcmVwbGllcyBhY3Jvc3MgYWxsIG1lZGlhIHR5cGVzCnRvdGFsX2F2ZXJhZ2Vfc3RhdHMgPC0gdHlwZXNfb2ZfdHdlZXRzX3Blcl90d2VldCAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKGF2Z192aWV3cyA9IHN1bShhdmdfdmlld3MpLAogICAgICAgICAgICBhdmdfbGlrZXMgPSBzdW0oYXZnX2xpa2VzKSwKICAgICAgICAgICAgYXZnX3JlcGxpZXMgPSBzdW0oYXZnX3JlcGxpZXMpKQoKcHJpbnQodG90YWxfYXZlcmFnZV9zdGF0cykKYGBgCgpgYGB7cn0KIyBDYWxjdWxhdGlvbiBvZiBsaWtlX3JhdGlvIGFuZCByZXBsaWVzX3JhdGlvIHBlcmNlbnRhZ2VzCnJhdGlvc19hdmVyYWdlX3RhYmxlIDwtIHRvdGFsX2F2ZXJhZ2Vfc3RhdHMgJT4lCiAgbXV0YXRlKGxpa2VfcmF0aW8gPSBhdmdfbGlrZXMgLyBhdmdfdmlld3MgKiAxMDAsCiAgICAgICAgIHJlcGxpZXNfcmF0aW8gPSBhdmdfcmVwbGllcyAvIGF2Z192aWV3cyAqIDEwMCkKCiMgQ3JlYXRpb24gb2YgbmV3IHRhYmxlIHdpdGggZWFjaCBIRUksIGxpa2VfcmF0aW8sIGFuZCByZXBsaWVzX3JhdGlvIApoZWlfYXZlcmFnZV9yYXRpb3MgPC0gcmF0aW9zX2F2ZXJhZ2VfdGFibGUgJT4lCiAgc2VsZWN0KGlkLCBsaWtlX3JhdGlvLCByZXBsaWVzX3JhdGlvKSAlPiUKICBkaXN0aW5jdCgpCgpwcmludChoZWlfYXZlcmFnZV9yYXRpb3MpCmBgYAoKIyBQbG90IGZvciBsaWtlX3JhdGlvIGFuZCByZXBsaWVzX3JhdGlvIGZvciBlYWNoIEhFSQoKYGBge3J9CmdncGxvdChoZWlfYXZlcmFnZV9yYXRpb3MsIGFlcyh4ID0gaWQpKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSBsaWtlX3JhdGlvLCBmaWxsID0gIkxpa2UgUmF0aW8iKSwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fYmFyKGFlcyh5ID0gcmVwbGllc19yYXRpbywgZmlsbCA9ICJSZXBsaWVzIFJhdGlvIiksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKHkgPSBsaWtlX3JhdGlvLCBsYWJlbCA9IHJvdW5kKGxpa2VfcmF0aW8sIDIpKSwgdmp1c3QgPSAtMC41LCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgc2l6ZSA9IDMsIGNvbG9yID0gIiMwMDAwMDAiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gcmVwbGllc19yYXRpbywgbGFiZWwgPSByb3VuZChyZXBsaWVzX3JhdGlvLCAyKSksIHZqdXN0ID0gLTAuNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHNpemUgPSAzLCBjb2xvciA9ICIjRkZGRkZGIikgKwogIGxhYnModGl0bGUgPSAiTGlrZSBhbmQgUmVwbGllcyBSYXRpb3MgYnkgSEVJIiwKICAgICAgIHggPSAiSEVJIiwKICAgICAgIHkgPSAiUmF0aW8gKCUpIiwKICAgICAgIGZpbGwgPSAiTWV0cmljIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkxpa2UgUmF0aW8iID0gIiMzMzAwNjYiLCAiUmVwbGllcyBSYXRpbyIgPSAiI0ZGNjY2NiIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkKYGBgCgojIEhhc2h0YWdzCgpgYGB7cn0KIyBUYWJsZSB3aXRoIG51bWJlciBvZiB1bmlxdWUgaGFzaHRhZ3MKdW5pcXVlX2hhc2h0YWdzIDwtIGRhdGFfdHdlZXRzICU+JQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JQogICAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZV9oYXNodGFncyA9IGxlbmd0aCh1bmlxdWUoaGFzaHRhZ3MpKSkKCnByaW50KHVuaXF1ZV9oYXNodGFncykKYGBgCgojIFBsb3QgZm9yIHRoZSBjb3VudCBvZiB1bmlxdWUgaGFzaHRhZ3MgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdCh1bmlxdWVfaGFzaHRhZ3MkdW5pcXVlX2hhc2h0YWdzLAogICAgICAgIG5hbWVzLmFyZyA9IHVuaXF1ZV9oYXNodGFncyRpZCwKICAgICAgICBtYWluID0gIlVuaXF1ZSBIYXNodGFncyBmb3IgRWFjaCBIRUkiLAogICAgICAgIHhsYWIgPSAiSEVJIiwKICAgICAgICB5bGFiID0gIkNvdW50IG9mIFVuaXF1ZSBIYXNodGFncyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHVuaXF1ZV9oYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MpICsgNTApLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sPSAiIzE2QTA4NSIpCgp0ZXh0KHggPSBiYXJwbG90KHVuaXF1ZV9oYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IHVuaXF1ZV9oYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MsCiAgICAgbGFiZWxzID0gcm91bmQodW5pcXVlX2hhc2h0YWdzJHVuaXF1ZV9oYXNodGFncywgMiksCiAgICAgcG9zID0gMykKYGBgCgojIEhlYXRtYXBzCgpgYGB7cn0KIyBDcmVhdGUgY29sdW1uIGhvdXIgZnJvbSBjcmVhdGVkX2F0CmRhdGFfdHdlZXRzX2RheXMkY3JlYXRlZF9ob3VyIDwtIGFzLm51bWVyaWMoZm9ybWF0KGRhdGFfdHdlZXRzX2RheXMkY3JlYXRlZF9hdCwgIiVIIikpCmBgYAoKIyBGdW5jdGlvbiB0byBwbG90IGhlYXRtYXAgZm9yIHZhcmlvdXMgSEVJcwoKYGBge3J9CmhlYXRtYXBfbWFrZXIgPC0gZnVuY3Rpb24odGFyZ2V0X2lkID0gImR1a2UuY3N2Iil7CiAgIyBGaWx0ZXJpbmcgZGF0YSBmb3IgdGhlIHNwZWNpZmljIEhFSQogIHRhcmdldF9kYXRhIDwtIGRhdGFfdHdlZXRzX2RheXMgJT4lCiAgICBmaWx0ZXIoaWQgPT0gdGFyZ2V0X2lkKQogIAogICMgR3JvdXBpbmcgYnkgZGF5IG9mIHRoZSB3ZWVrIGFuZCBob3VyLCBhbmQgY291bnRpbmcgdGhlIG51bWJlciBvZiB0d2VldHMKICB0d2VldF9jb3VudHMgPC0gdGFyZ2V0X2RhdGEgJT4lCiAgICBncm91cF9ieShkYXlfb2Zfd2VlaywgY3JlYXRlZF9ob3VyKSAlPiUKICAgIHN1bW1hcmlzZShudW1fdHdlZXRzID0gbigpKQogIAogICMgUGxvdHRpbmcgaGVhdG1hcAogIGdncGxvdCh0d2VldF9jb3VudHMsIGFlcyh4ID0gZGF5X29mX3dlZWssIHkgPSBjcmVhdGVkX2hvdXIsIGZpbGwgPSBudW1fdHdlZXRzKSkgKwogICAgZ2VvbV90aWxlKCkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIlR3ZWV0IEhlYXRtYXAgZm9yIiwgdGFyZ2V0X2lkKSwKICAgICAgICAgeCA9ICJEYXkgb2YgdGhlIHdlZWsiLAogICAgICAgICB5ID0gIkhvdXIgb2YgdGhlIGRheSIpCn0KCmhlYXRtYXBfbWFrZXIoKQpoZWF0bWFwX21ha2VyKCJlcGZsLmNzdiIpCmhlYXRtYXBfbWFrZXIoImdvZS5jc3YiKQpoZWF0bWFwX21ha2VyKCJoYXJ2YXJkLmNzdiIpCmhlYXRtYXBfbWFrZXIoImxlaWNlc3Rlci5jc3YiKQpoZWF0bWFwX21ha2VyKCJtYW5jaGVzdGVyLmNzdiIpCmhlYXRtYXBfbWFrZXIoIm1pdC5jc3YiKQpoZWF0bWFwX21ha2VyKCJzYi5jc3YiKQpoZWF0bWFwX21ha2VyKCJzdGFuZm9yZC5jc3YiKQpoZWF0bWFwX21ha2VyKCJ0cmluaXR5LmNzdiIpCmhlYXRtYXBfbWFrZXIoInd2LmNzdiIpCmhlYXRtYXBfbWFrZXIoInlhbGUuY3N2IikKYGBgCgojIFRleHQKCmBgYHtyfQpkYXRhX3R3ZWV0c19jb250ZW50IDwtIGRhdGFfdHdlZXRzICU+JQogICAgICAgICAgICBzZWxlY3QoaWQsIHRleHQpCgojIENvdW50aW5nIG51bWJlciBvZiB3b3JkcwpkYXRhX3R3ZWV0c19jb250ZW50IDwtIGRhdGFfdHdlZXRzX2NvbnRlbnQgJT4lCiAgbXV0YXRlKG51bV93b3JkcyA9IGxlbmd0aHMoc3Ryc3BsaXQodGV4dCwgIlxccysiKSkpCgpwcmludChkYXRhX3R3ZWV0c19jb250ZW50KQoKIyBHcm91cGluZyBieSBIRUkgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlLCBtaW5pbXVtLCBhbmQgbWF4aW11bSB2YWx1ZXMgb2YgbnVtYmVyIG9mIHdvcmRzCmRhdGFfdHdlZXRzX2NvbnRlbnRfbWV0cmljcyA8LSBkYXRhX3R3ZWV0c19jb250ZW50ICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UoYXZlcmFnZV9udW1fd29yZHMgPSBtZWFuKG51bV93b3JkcyksCiAgICAgICAgICAgIG1pbl9udW1fd29yZHMgPSBtaW4obnVtX3dvcmRzKSwKICAgICAgICAgICAgbWF4X251bV93b3JkcyA9IG1heChudW1fd29yZHMpKQpwcmludChkYXRhX3R3ZWV0c19jb250ZW50X21ldHJpY3MpCmBgYAoKIyBQbG90IGZvciB0aGUgYXZlcmFnZSwgbWF4aW11bSBhbmQgbWluaW11bSB2YWx1ZXMgb2Ygd29yZHMgZm9yIGVhY2ggSEVJCgpgYGB7cn0KZ2dwbG90KGRhdGFfdHdlZXRzX2NvbnRlbnRfbWV0cmljcywgYWVzKHggPSBpZCwgeSA9IGF2ZXJhZ2VfbnVtX3dvcmRzKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gIkF2ZXJhZ2UiKSkgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBtaW5fbnVtX3dvcmRzLCB5bWF4ID0gbWF4X251bV93b3JkcywgY29sb3IgPSAiUmFuZ2UiKSwgd2lkdGggPSAwLjIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiQXZlcmFnZSIgPSAiIzE5NzZEMiIsICJSYW5nZSIgPSAiI0VGNTM1MCIpKSArCiAgbGFicyh0aXRsZSA9ICJXb3JkIENvdW50IFN1bW1hcnkgYnkgSEVJIiwKICAgICAgIHggPSAiSEVJIiwKICAgICAgIHkgPSAiTnVtYmVyIG9mIFdvcmRzIiwKICAgICAgIGNvbG9yID0gIk1ldHJpYyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKQpgYGAKCiMgTm93IHJlcGxpZXMgCgpgYGB7cn0KZGF0YV9yZXBsaWVzIDwtIGRhdGFbZGF0YSR0eXBlID09ICJSZXBseSIsIF0KCmRhdGFfcmVwbGllcwpgYGAKCiMgSW50ZXJhY3Rpb25zIHRvIHJlcGxpZXMKCmBgYHtyfQojIFRhYmxlIGNvbnRhaW5pbmcgdmlld3MsIGxpa2VzLCByZXR3ZWV0cyBhbmQgcmVwbGllcyBmb3IgZWFjaCBtZWRpYSB0eXBlIGZvciBlYWNoIEhFSQp0eXBlc19vZl9yZXBsaWVzIDwtIGRhdGFfcmVwbGllcyAlPiUKICAgICAgICAgICAgICBncm91cF9ieShpZCwgbWVkaWFfdHlwZSkgJT4lCiAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICAgICAgICAgICAgICB2aWV3cyA9IHN1bSh2aWV3X2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICBsaWtlcyA9IHN1bShmYXZvcml0ZV9jb3VudCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgcmV0d2VldHMgPSBzdW0ocmV0d2VldF9jb3VudCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGllcyA9IHN1bShyZXBseV9jb3VudCwgbmEucm0gPSBUUlVFKSkKICAgICAgICAgICAgICAgICAgICAgICAgCnByaW50KHR5cGVzX29mX3JlcGxpZXMpICAgICAgICAgICAgICAgICAgICAgICAgCmBgYAoKYGBge3J9CiMgR3JvdXBpbmcgYnkgSEVJIGFuZCBjYWxjdWxhdGluZyB0aGUgdG90YWwgdmFsdWVzIG9mIHZpZXdzLCBsaWtlcyBhbmQgcmVwbGllcyBhY3Jvc3MgYWxsIG1lZGlhIHR5cGVzCnRvdGFsX3JlcGxpZXNfc3RhdHMgPC0gdHlwZXNfb2ZfcmVwbGllcyAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKHRvdGFsX3ZpZXdzID0gc3VtKHZpZXdzKSwKICAgICAgICAgICAgdG90YWxfbGlrZXMgPSBzdW0obGlrZXMpLAogICAgICAgICAgICB0b3RhbF9yZXBsaWVzID0gc3VtKHJlcGxpZXMpKQoKcHJpbnQodG90YWxfcmVwbGllc19zdGF0cykKYGBgCgpgYGB7cn0KIyBDYWxjdWxhdGlvbiBvZiBsaWtlX3JhdGlvIGFuZCByZXBsaWVzX3JhdGlvIHBlcmNlbnRhZ2VzCnJhdGlvc19yZXBsaWVzX3RhYmxlIDwtIHRvdGFsX3JlcGxpZXNfc3RhdHMgJT4lCiAgbXV0YXRlKGxpa2VfcmF0aW8gPSB0b3RhbF9saWtlcyAvIHRvdGFsX3ZpZXdzICogMTAwLAogICAgICAgICByZXBsaWVzX3JhdGlvID0gdG90YWxfcmVwbGllcyAvIHRvdGFsX3ZpZXdzICogMTAwKQoKIyBDcmVhdGlvbiBvZiBuZXcgdGFibGUgd2l0aCBlYWNoIEhFSSwgbGlrZV9yYXRpbywgYW5kIHJlcGxpZXNfcmF0aW8gCmhlaV9yZXBsaWVzX3JhdGlvcyA8LSByYXRpb3NfcmVwbGllc190YWJsZSAlPiUKICBzZWxlY3QoaWQsIGxpa2VfcmF0aW8sIHJlcGxpZXNfcmF0aW8pICU+JQogIGRpc3RpbmN0KCkKCnByaW50KGhlaV9yZXBsaWVzX3JhdGlvcykKYGBgCgojIENsdXN0ZXJzCgpgYGB7cn0KIyBDcmVhdGluZyB0YWJsZSBmb3IgY2x1c3RlciBhbGdvcml0aG1zCgojIEpvaW5pbmcgYXR0cmlidXRlIGNvdW50IChudW1iZXIgb2YgdHdlZXRzKSBhbmQgdW5pcXVlX2hhc2h0YWdzIChudW1iZXIgb2YgdW5pcXVlIGhhc2h0YWdzKSBwZXIgSEVJCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2Uoc2VsZWN0KHVuaXF1ZV9oYXNodGFncywgaWQsIHVuaXF1ZV9oYXNodGFncyksIHNlbGVjdChudW1iZXJfdHdlZXRzLCBpZCwgY291bnQpLCBieSA9ICJpZCIsIGFsbD1UUlVFKQoKIyBKb2luaW5nIGF0dHJpYnV0ZSBhdmdfdHdlZXRzX3Blcl9kYXlzIChhdmVyYWdlIG9mIHR3ZWV0cyBwZXIgZGF5KSBwZXIgSEVJCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2UoY2x1c3Rlcl90YWJsZSwgc2VsZWN0KHR3ZWV0c19wZXJfZGF5LCBpZCwgYXZnX3R3ZWV0c19wZXJfZGF5cyksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z190d2VldHNfcGVyX3dlZWtzIChhdmVyYWdlIG9mIHR3ZWV0cyBwZXIgd2VlaykgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdCh0d2VldHNfcGVyX3dlZWssIGlkLCBhdmdfdHdlZXRzX3Blcl93ZWVrcyksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSAoYXZlcmFnZSBvZiB0d2VldHMgZHVyaW5nIGFjYWRlbWljIHRpbWUpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QoZGF0YV90d2VldHNfYWNhZGVtaWMsIGlkLCBhdmdfdHdlZXRzX2luX2FjYWRlbWljX3RpbWUpLCBieSA9ICJpZCIsIGFsbD1UUlVFKQoKIyBKb2luaW5nIGF0dHJpYnV0ZSBhdmdfdHdlZXRzX2luX3ZhY2F0aW9uX3RpbWUgKGF2ZXJhZ2Ugb2YgdHdlZXRzIGR1cmluZyB2YWNhdGlvbiB0aW1lKSBwZXIgSEVJCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2UoY2x1c3Rlcl90YWJsZSwgc2VsZWN0KGRhdGFfdHdlZXRzX3ZhY2F0aW9ucywgaWQsIGF2Z190d2VldHNfaW5fdmFjYXRpb25fdGltZSksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIHRvdGFsX3ZpZXdzICh0b3RhbCBudW1iZXIgb2Ygdmlld3MpLCB0b3RhbF9saWtlcyAodG90YWwgbnVtYmVyIG9mIGxpa2VzKSBhbmQgdG90YWxfcmVwbGllcyAodG90YWwgbnVtYmVyIG9mIHJlcGxpZXMpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QodG90YWxfdHdlZXRzX3N0YXRzLCBpZCwgdG90YWxfdmlld3MsIHRvdGFsX2xpa2VzLCB0b3RhbF9yZXBsaWVzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgUmVuYW1pbmcgYXR0cmlidXRlIGxpa2VfcmF0aW8gdG8gdG90YWxfbGlrZV9yYXRpbyBhbmQgcmVwbGllc19yYXRpbyB0byB0b3RhbF9yZXBsaWVzX3JhdGlvCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2UoY2x1c3Rlcl90YWJsZSwgc2VsZWN0KGhlaV90d2VldHNfcmF0aW9zLCBpZCwgbGlrZV9yYXRpbywgcmVwbGllc19yYXRpbyksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCmNsdXN0ZXJfdGFibGUgPC0gY2x1c3Rlcl90YWJsZSAlPiUKICByZW5hbWUodG90YWxfbGlrZV9yYXRpbyA9IGxpa2VfcmF0aW8sIAogICAgICAgICB0b3RhbF9yZXBsaWVzX3JhdGlvID0gcmVwbGllc19yYXRpbykKCiMgSm9pbmluZyBhdHRyaWJ1dGUgYXZnX3ZpZXdzIChhdmVyYWdlIG51bWJlciBvZiB2aWV3cyksIGF2Z19saWtlcyAoYXZlcmFnZSBudW1iZXIgb2YgbGlrZXMpIGFuZCBhdmdfcmVwbGllcyAoYXZlcmFnZSBudW1iZXIgb2YgcmVwbGllcykgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdCh0b3RhbF9hdmVyYWdlX3N0YXRzLCBpZCwgYXZnX3ZpZXdzLCBhdmdfbGlrZXMsIGF2Z19yZXBsaWVzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgUmVuYW1pbmcgYXR0cmlidXRlIGxpa2VfcmF0aW8gdG8gYXZnX2xpa2VfcmF0aW8gYW5kIHJlcGxpZXNfcmF0aW8gdG8gYXZnX3JlcGxpZXNfcmF0aW8KY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QoaGVpX2F2ZXJhZ2VfcmF0aW9zLCBpZCwgbGlrZV9yYXRpbywgcmVwbGllc19yYXRpbyksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCmNsdXN0ZXJfdGFibGUgPC0gY2x1c3Rlcl90YWJsZSAlPiUKICByZW5hbWUoYXZnX2xpa2VfcmF0aW8gPSBsaWtlX3JhdGlvLCAKICAgICAgICAgYXZnX3JlcGxpZXNfcmF0aW8gPSByZXBsaWVzX3JhdGlvKQoKcHJpbnQoY2x1c3Rlcl90YWJsZSkKYGBgCgojIEZ1bmN0aW9uIGZvciBjbHVzdGVyIG1ldGhvZAoKYGBge3J9CmNsdXN0ZXJfbWFrZXIgPC0gZnVuY3Rpb24oc2VlZCA9IDEyMywgbnVtX2NsdXN0ZXJzID0gMywgdGFibGUpewogIHNldC5zZWVkKDEyMykKICAKICAjIEV4Y2x1ZGluZyBpZCBjb2x1bW4gZm9yIGNsdXN0ZXJpbmcKICBjbHVzdGVyX2RhdGEgPC0gc2VsZWN0KHRhYmxlLCAtaWQpCiAgCiAgIyBTY2FsaW5nIHRoZSBkYXRhIGZvciBrbWVhbnMgbWV0aG9kCiAgc2NhbGVkX2RhdGEgPC0gc2NhbGUoY2x1c3Rlcl9kYXRhKQogIAogIGttZWFuc19yZXN1bHQgPC0ga21lYW5zKHNjYWxlZF9kYXRhLCBjZW50ZXJzID0gbnVtX2NsdXN0ZXJzKQogIAogIHByaW50KGttZWFuc19yZXN1bHQkY2VudGVycykKICBwcmludChrbWVhbnNfcmVzdWx0JGNsdXN0ZXIpCiAgCiAgcmV0dXJuKGttZWFuc19yZXN1bHQpCn0KYGBgCgojIEZ1bmN0aW9uIHRvIGFkZCBpZHMgdG8gYmV0dGVyIHZpc3VhbGl6ZSByZXN1bHRzCgpgYGB7cn0KY2x1c3Rlcl9pZF9tYWtlciA8LSBmdW5jdGlvbihrbWVhbnNfcmVzdWx0LCB0YWJsZSl7CiAgIyBNZXJnaW5nIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnRzIHdpdGggdGhlIG9yaWdpbmFsIGRhdGEKICBjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGRhdGEuZnJhbWUoaWQgPSB0YWJsZSRpZCwgY2x1c3RlciA9IGttZWFuc19yZXN1bHQkY2x1c3RlcikKCiAgcHJpbnQoY2x1c3Rlcl9hc3NpZ25tZW50cykKICBwbG90KGttZWFuc19yZXN1bHQkY2x1c3RlcikKfQpgYGAKCiMgVGhyZWUgY2x1c3RlcnMgd2l0aCBzZWVkIDEyMwoKYGBge3J9CmNsdXN0ZXJfMTIzXzMgPC0gY2x1c3Rlcl9tYWtlcih0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmNsdXN0ZXJfaWRfbWFrZXIoY2x1c3Rlcl8xMjNfMywgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpgYGAKCiMgU2V2ZW4gY2x1c3RlcnMgd2l0aCBzZWVkIDEyMwoKYGBge3J9CmNsdXN0ZXJfMTIzXzYgPC0gY2x1c3Rlcl9tYWtlcihudW1fY2x1c3RlcnMgPSA3LCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmNsdXN0ZXJfaWRfbWFrZXIoY2x1c3Rlcl8xMjNfNiwgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpgYGAKCiMgRml2ZSBjbHVzdGVycyB3aXRoIHNlZWQgMTIzCgpgYGB7cn0KY2x1c3Rlcl8xMjNfNiA8LSBjbHVzdGVyX21ha2VyKG51bV9jbHVzdGVycyA9IDUsIHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKY2x1c3Rlcl9pZF9tYWtlcihjbHVzdGVyXzEyM182LCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmBgYAoKIyBGb3VyIGNsdXN0ZXJzIHdpdGggc2VlZCA0ODU1CgpgYGB7cn0KY2x1c3Rlcl8xMjNfMyA8LSBjbHVzdGVyX21ha2VyKHNlZWQgPSA0ODU1LCBudW1fY2x1c3RlcnMgPSA0LCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmNsdXN0ZXJfaWRfbWFrZXIoY2x1c3Rlcl8xMjNfMywgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpgYGAKCiMgU2l4IGNsdXN0ZXJzIHdpdGggc2VlZCA0ODU1CgpgYGB7cn0KY2x1c3Rlcl8xMjNfNiA8LSBjbHVzdGVyX21ha2VyKHNlZWQgPSA0ODU1LCBudW1fY2x1c3RlcnMgPSA2LCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmNsdXN0ZXJfaWRfbWFrZXIoY2x1c3Rlcl8xMjNfNiwgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpgYGAKCg==